spring cache

spring cache 學(xué)習(xí)文檔

1, spring cache 的使用

1.1 依賴導(dǎo)入

    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

1.2 代碼示例

@RestController
@RequestMapping("/books")
public class BookController {

    @Cacheable(value = "books", key = "#role+':'+#id")
    @GetMapping("/{id}")
    public Book getBook(@PathVariable("id") String id, @RequestHeader("role") String role) {

        sleep(5000);
        Book book = MockUtils.selectById(id);

        return book;
    }

    @PostMapping
    @Caching(put = {@CachePut(value = "books", key = "#role+':'+#book.id")}, evict = {@CacheEvict(cacheNames = {"book2"}, allEntries = true)})
    public Book updateById(@RequestBody Book book, @RequestHeader("role") String role) {

        MockUtils.updateById(book);
        return book;
    }

    @Cacheable(cacheNames = {"book2", "book3"}, key = "#arg")
    @GetMapping("/all")
    public Object getAll(String arg) {
        sleep(5000);

        return MockUtils.books();
    }

    @CacheEvict(cacheNames = {"book3"},allEntries = true)
    @GetMapping("/clear")
    public void clear() {

    }
    @SneakyThrows
    public void sleep(long time) {
        Thread.sleep(time);

    }
}

1.3 cache 注解介紹

Spring Cache 注解是 Spring 框架提供的一種方便的方式,用于簡化應(yīng)用程序中的緩存操作。通過使用這些注解,您可以輕松地將方法結(jié)果緩存起來,以提高應(yīng)用程序的性能。以下是 Spring Cache 注解的使用介紹:

  1. @EnableCaching: 這個注解通常用于配置類上,用于啟用 Spring Cache 支持。它告訴 Spring 框架要啟用緩存功能。配置類上使用 @EnableCaching 后,您可以在其他組件中使用緩存注解。

  2. @Cacheable: 這個注解用于標(biāo)記方法,表示該方法的結(jié)果應(yīng)該被緩存。您可以指定一個或多個緩存名稱,以及一個緩存鍵(可使用 SpEL 表達(dá)式動態(tài)生成)。當(dāng)方法被調(diào)用時,Spring 框架會首先檢查緩存,如果找到緩存值,則直接返回緩存值,而不執(zhí)行方法體。如果緩存中沒有找到值,方法會執(zhí)行,結(jié)果將被緩存。

    示例:

    
    @Cacheable(value = "books", key = "#id")
    public Book getBook(String id) {
        // ...
    }
    
  3. @CacheEvict: 這個注解用于標(biāo)記方法,表示該方法會清除一個或多個緩存中的值。您可以指定一個或多個緩存名稱,以及一個緩存鍵(可使用 SpEL 表達(dá)式動態(tài)生成)。當(dāng)方法執(zhí)行后,它會清除指定緩存中的值。

    示例:

    
    @CacheEvict(value = "books", key = "#id")
    public void deleteBook(String id) {
        // ...
    }
    
  4. @CachePut: 這個注解用于標(biāo)記方法,表示該方法會將結(jié)果放入緩存。與 @Cacheable 不同,@CachePut 無論緩存中是否已存在相同的鍵,都會執(zhí)行方法體并將結(jié)果放入緩存。

    示例:

    
    @CachePut(value = "books", key = "#book.id")
    public Book updateBook(Book book) {
        // ...
    }
    
  5. @Caching: 這個注解允許您同時應(yīng)用多個緩存注解在同一個方法上。您可以在 @Caching 注解中包含多個 @Cacheable, @CacheEvict, 或 @CachePut 注解,以定義多個緩存操作。

    示例:

    javaCopy code
    @Caching(put = {@CachePut(value = "books", key = "#book.id")}, evict = {@CacheEvict(value = "books", allEntries = true)})
    public void saveOrUpdateBook(Book book) {
        // ...
    }
    
  6. @CacheConfig: 這個注解通常用于配置類上,用于指定緩存的默認(rèn)配置。您可以在該類中使用 @Cacheable、@CacheEvict 等注解時,省略緩存名稱等配置,因為它們會繼承自 @CacheConfig。

    示例:

    
    @CacheConfig(cacheNames = "books")
    public class BookService {
        // ...
    }
    

這些注解使緩存配置更加簡單,讓您可以通過注解方式輕松地控制緩存操作。通過在適當(dāng)?shù)姆椒ㄉ咸砑舆@些注解,您可以提高應(yīng)用程序的性能,尤其是對于需要頻繁訪問的數(shù)據(jù)

1.4 緩存 key 的生成

@FunctionalInterface
public interface KeyGenerator {

    /**
     * Generate a key for the given method and its parameters.
     * @param target the target instance
     * @param method the method being called
     * @param params the method parameters (with any var-args expanded)
     * @return a generated key
     */
    Object generate(Object target, Method method, Object... params);

}

2 spring cache 源碼解析

2.1 開啟注解

2.2 配置類引入

2.2.1 CachingConfigurationSelector

public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {

    private static final String PROXY_JCACHE_CONFIGURATION_CLASS =
            "org.springframework.cache.jcache.config.ProxyJCacheConfiguration";

    private static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
            "org.springframework.cache.aspectj.AspectJCachingConfiguration";

    private static final String JCACHE_ASPECT_CONFIGURATION_CLASS_NAME =
            "org.springframework.cache.aspectj.AspectJJCacheConfiguration";


    private static final boolean jsr107Present;

    private static final boolean jcacheImplPresent;

    static {
        ClassLoader classLoader = CachingConfigurationSelector.class.getClassLoader();
        jsr107Present = ClassUtils.isPresent("javax.cache.Cache", classLoader);
        jcacheImplPresent = ClassUtils.isPresent(PROXY_JCACHE_CONFIGURATION_CLASS, classLoader);
    }


    /**
     * Returns {@link ProxyCachingConfiguration} or {@code AspectJCachingConfiguration}
     * for {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()},
     * respectively. Potentially includes corresponding JCache configuration as well.
     */
    @Override
    public String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return getProxyImports();
            case ASPECTJ:
                return getAspectJImports();
            default:
                return null;
        }
    }

    /**
     * Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#PROXY}.
     * <p>Take care of adding the necessary JSR-107 import if it is available.
     */
    private String[] getProxyImports() {
        List<String> result = new ArrayList<>(3);
        result.add(AutoProxyRegistrar.class.getName());
        result.add(ProxyCachingConfiguration.class.getName());
        if (jsr107Present && jcacheImplPresent) {
            result.add(PROXY_JCACHE_CONFIGURATION_CLASS);
        }
        return StringUtils.toStringArray(result);
    }

    /**
     * Return the imports to use if the {@link AdviceMode} is set to {@link AdviceMode#ASPECTJ}.
     * <p>Take care of adding the necessary JSR-107 import if it is available.
     */
    private String[] getAspectJImports() {
        List<String> result = new ArrayList<>(2);
        result.add(CACHE_ASPECT_CONFIGURATION_CLASS_NAME);
        if (jsr107Present && jcacheImplPresent) {
            result.add(JCACHE_ASPECT_CONFIGURATION_CLASS_NAME);
        }
        return StringUtils.toStringArray(result);
    }

}

2.2.2 ProxyCachingConfiguration 默認(rèn)

 @Configuration
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
  
    @Bean(name = CacheManagementConfigUtils.CACHE_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
    //advistor 顧問 ,負(fù)責(zé)把 
    切點(diǎn) pointcut(用來指定需要將通知使用到哪些地方,比如需要用在哪些類的哪些方法上,切入點(diǎn)就是做這個配置的。
  ) 和 
  advice(需要增強(qiáng)的功能)
        BeanFactoryCacheOperationSourceAdvisor advisor = new BeanFactoryCacheOperationSourceAdvisor();
        advisor.setCacheOperationSource(cacheOperationSource());
        advisor.setAdvice(cacheInterceptor());
        if (this.enableCaching != null) {
            advisor.setOrder(this.enableCaching.<Integer>getNumber("order"));
        }
        return advisor;
    }
  
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheOperationSource cacheOperationSource() {
        return new AnnotationCacheOperationSource();
    }
  
    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public CacheInterceptor cacheInterceptor() {
        CacheInterceptor interceptor = new CacheInterceptor();
        interceptor.configure(this.errorHandler, this.keyGenerator, this.cacheResolver, this.cacheManager);
        interceptor.setCacheOperationSource(cacheOperationSource());
        return interceptor;
    }
  
  }
  • AbstractCachingConfiguration : ProxyCachingConfiguration 的父類
@Configuration
public abstract class AbstractCachingConfiguration implements ImportAware {

    @Nullable
    protected AnnotationAttributes enableCaching;

    @Nullable
    protected Supplier<CacheManager> cacheManager;
    
    @Nullable
    protected Supplier<CacheResolver> cacheResolver;
    
    @Nullable
    protected Supplier<KeyGenerator> keyGenerator;
    
    @Nullable
    protected Supplier<CacheErrorHandler> errorHandler;

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        this.enableCaching = AnnotationAttributes.fromMap(
                importMetadata.getAnnotationAttributes(EnableCaching.class.getName(), false));
        if (this.enableCaching == null) {
            throw new IllegalArgumentException(
                    "@EnableCaching is not present on importing class " + importMetadata.getClassName());
        }
    }

    @Autowired(required = false)
    void setConfigurers(Collection<CachingConfigurer> configurers) {
        if (CollectionUtils.isEmpty(configurers)) {
            return;
        }
        if (configurers.size() > 1) {
            throw new IllegalStateException(configurers.size() + " implementations of " +
                    "CachingConfigurer were found when only 1 was expected. " +
                    "Refactor the configuration such that CachingConfigurer is " +
                    "implemented only once or not at all.");
        }
        CachingConfigurer configurer = configurers.iterator().next();
        useCachingConfigurer(configurer);
    }

    /**
     * Extract the configuration from the nominated {@link CachingConfigurer}.
     */
    protected void useCachingConfigurer(CachingConfigurer config) {
        this.cacheManager = config::cacheManager;
        this.cacheResolver = config::cacheResolver;
        this.keyGenerator = config::keyGenerator;
        this.errorHandler = config::errorHandler;
    }

}

  • CacheOperationSourcePointcut: 配置切入點(diǎn)
  • CacheOperationSource: 解析方法緩存配置用的
  • BeanFactoryCacheOperationSourceAdvisor :

    1. CacheOperationSourcePointcut :

    2. matches 最終調(diào)用的是CacheOperationSource 的方法 即系帶有

      public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
      
         private static final Set<Class<? extends Annotation>> CACHE_OPERATION_ANNOTATIONS = new LinkedHashSet<>(8);
      
         static {
             CACHE_OPERATION_ANNOTATIONS.add(Cacheable.class);
             CACHE_OPERATION_ANNOTATIONS.add(CacheEvict.class);
             CACHE_OPERATION_ANNOTATIONS.add(CachePut.class);
             CACHE_OPERATION_ANNOTATIONS.add(Caching.class);
         }
         @Override
         public boolean isCandidateClass(Class<?> targetClass) {
             return AnnotationUtils.isCandidateClass(targetClass, CACHE_OPERATION_ANNOTATIONS);
         }
      
 ```
 abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
      //過濾類
    protected CacheOperationSourcePointcut() {
        setClassFilter(new CacheOperationSourceClassFilter());
    }
    @Override
    //過濾方法
    public boolean matches(Method method, Class<?> targetClass) {
        CacheOperationSource cas = getCacheOperationSource();
        return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
    }
    protected abstract CacheOperationSource getCacheOperationSource();
  
    private class CacheOperationSourceClassFilter implements ClassFilter {
 
        @Override
        public boolean matches(Class<?> clazz) {
            if (CacheManager.class.isAssignableFrom(clazz)) {
                return false;
            }
            CacheOperationSource cas = getCacheOperationSource();
            return (cas == null || cas.isCandidateClass(clazz));
        }
    }
 
 }
 
 ```

2.3 aop 相關(guān)

2.3.1 cache 對應(yīng)的 advistor

public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

  @Nullable
  private CacheOperationSource cacheOperationSource;

  private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
      @Override
      @Nullable
      protected CacheOperationSource getCacheOperationSource() {
          return cacheOperationSource;
      }
  };

  public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
      this.cacheOperationSource = cacheOperationSource;
  }

  public void setClassFilter(ClassFilter classFilter) {
      this.pointcut.setClassFilter(classFilter);
  }

  @Override
  public Pointcut getPointcut() {
      return this.pointcut;
  }

}

2.3 .2 aop 的 MethodInterceptor 方法攔截器

public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
    @Override
    @Nullable
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();

        CacheOperationInvoker aopAllianceInvoker = () -> {
            try {
                return invocation.proceed();
            }
            catch (Throwable ex) {
                throw new CacheOperationInvoker.ThrowableWrapper(ex);
            }
        };
        try {
        // CacheAspectSupport 提供通用邏輯
            return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
        }
        catch (CacheOperationInvoker.ThrowableWrapper th) {
            throw th.getOriginal();
        }
    }

}

2.3.3 , CacheOperationInvoker

實際上只是個函數(shù)式接口 用來開執(zhí)行 代理對象的MethodInvocation.proceed 方法,來獲取返回值

 
@FunctionalInterface
public interface CacheOperationInvoker {

     
     */
    Object invoke() throws ThrowableWrapper;


    /**
     * Wrap any exception thrown while invoking {@link #invoke()}.
     */
    @SuppressWarnings("serial")
    class ThrowableWrapper extends RuntimeException {

        private final Throwable original;

        public ThrowableWrapper(Throwable original) {
            super(original.getMessage(), original);
            this.original = original;
        }

        public Throwable getOriginal() {
            return this.original;
        }
    }

}

2 .3.4 CacheAspectSupport : 用來處理緩存的操作

  • CacheOperationSource 用于獲取緩存配置信息

    [圖片上傳失敗...(image-204870-1697073547857)]

  • BasicOperation:

[圖片上傳失敗...(image-8ac017-1697073547857)]

  • CacheOperation: 對應(yīng)于緩存注解配置信息

[圖片上傳失敗...(image-be5fe2-1697073547857)]

  • CachePutRequest: 實際上就是獲取cache ,然后根據(jù)key 來更新緩存

    private class CachePutRequest {

        private final CacheOperationContext context;

        private final Object key;

        public CachePutRequest(CacheOperationContext context, Object key) {
            this.context = context;
            this.key = key;
        }

        public void apply(@Nullable Object result) {
            if (this.context.canPutToCache(result)) {
                for (Cache cache : this.context.getCaches()) {
                //更新緩存
                    doPut(cache, this.key, result);
                }
            }
        }
    }
    @Nullable
    protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
        // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
        if (this.initialized) {
            Class<?> targetClass = getTargetClass(target);
            CacheOperationSource cacheOperationSource = getCacheOperationSource();
            if (cacheOperationSource != null) {
                Collection<CacheOperation> operations = cacheOperationSource.getCacheOperations(method, targetClass);
                if (!CollectionUtils.isEmpty(operations)) {
                    return execute(invoker, method,
                            new CacheOperationContexts(operations, method, args, target, targetClass));
                }
            }
        }

        return invoker.invoke();
    }
    @Nullable
    private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
         // 方法調(diào)用前的緩存過期處理,設(shè)置了CacheEvict.isBeforeInvocation屬性時
processCacheEvicts(contexts.get(CacheEvictOperation.class), true,
        CacheOperationExpressionEvaluator.NO_RESULT);

// Cacheable注解時,去查找緩存
Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

// 緩存未命中時,把Cacheable加入到CachePut的集合,后面用于寫入緩存
List<CachePutRequest> cachePutRequests = new LinkedList<>();
if (cacheHit == null) {
    collectPutRequests(contexts.get(CacheableOperation.class),
            CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests);
}

Object cacheValue;
Object returnValue;

if (cacheHit != null && !hasCachePut(contexts)) {
    // If there are no put requests, just use the cache hit
    cacheValue = cacheHit.get();
    returnValue = wrapCacheValue(method, cacheValue);
}
else {
    // 未找到緩存時,調(diào)用目標(biāo)方法
    returnValue = invokeOperation(invoker);
    cacheValue = unwrapReturnValue(returnValue);
}

// 收集CachePut注解,后面寫入緩存
collectPutRequests(contexts.get(CachePutOperation.class), cacheValue, cachePutRequests);

// 寫入緩存
for (CachePutRequest cachePutRequest : cachePutRequests) {
    cachePutRequest.apply(cacheValue);
}

// 處理緩存過期邏輯
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, cacheValue);
// 返回結(jié)果給代理類
return returnValue;
    }

3 spring cache 落地

3.1 spring cache 集成 caffeine

3.1.1 caffeine 介紹

Caffeine 是一個高性能的 Java 緩存庫,用于在應(yīng)用程序中管理內(nèi)存緩存。它提供了許多功能和優(yōu)化,以幫助應(yīng)用程序提高數(shù)據(jù)的訪問速度和降低資源消耗。以下是 Caffeine 的一些主要特點(diǎn)和優(yōu)勢:

  1. 高性能: Caffeine 被設(shè)計成一個高性能的緩存庫,具有低延遲和高吞吐量。它通過各種算法和數(shù)據(jù)結(jié)構(gòu)的使用來優(yōu)化緩存操作,以確??焖俚臄?shù)據(jù)訪問。
  2. 內(nèi)存管理: Caffeine 具有靈活的內(nèi)存管理功能,可以控制緩存的容量和淘汰策略。您可以指定緩存的最大容量、最大條目數(shù)、過期時間等,以滿足您的應(yīng)用程序需求。
  3. 并發(fā)支持: Caffeine 是線程安全的,支持高并發(fā)訪問。它使用先進(jìn)的并發(fā)數(shù)據(jù)結(jié)構(gòu)來確保多線程環(huán)境下的數(shù)據(jù)一致性和性能。
  4. 加載和刷新策略: 您可以定義自定義的加載策略,以便在緩存中沒有找到數(shù)據(jù)時加載數(shù)據(jù)。還可以定義自動刷新策略,以確保緩存中的數(shù)據(jù)保持新鮮。
  5. 監(jiān)聽器支持: Caffeine 允許您注冊監(jiān)聽器以監(jiān)聽緩存事件,如數(shù)據(jù)加載、數(shù)據(jù)移除等。
  6. 統(tǒng)計信息: Caffeine 提供了豐富的緩存統(tǒng)計信息,幫助您了解緩存的使用情況和性能。
  7. 自定義策略: 您可以自定義各種策略,包括淘汰策略、加載策略和刷新策略,以滿足不同的使用場景。
  8. 輕量級: Caffeine 是一個輕量級的庫,沒有過多的依賴,易于集成到各種 Java 項目中。
  9. 廣泛使用: Caffeine 被廣泛用于各種應(yīng)用程序中,包括大型企業(yè)級應(yīng)用、微服務(wù)架構(gòu)、桌面應(yīng)用程序等。

總的來說,Caffeine 是一個強(qiáng)大的緩存庫,適用于需要高性能和可配置性的應(yīng)用程序,它提供了豐富的功能和優(yōu)化,可以顯著提高應(yīng)用程序的性能。

3.1.2 caffeine 依賴引入

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.9.1</version> <!-- 按需替換為最新版本 -->
</dependency>

3.1.3 配置 CaffeineCacheManager

 @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
        caffeineCacheManager.setCaffeine(Caffeine.newBuilder()
                .expireAfterWrite(30, TimeUnit.SECONDS)
                .initialCapacity(100)
                .maximumSize(10_000));
        caffeineCacheManager.setCacheLoader(new CacheLoader<Object, Object>() {
            @Override
            public @Nullable Object load(@NonNull Object key) throws Exception {

                log.info("數(shù)據(jù)過期了");
                return null;
            }
        });

        return caffeineCacheManager;
    }

3.1.4 給不同的cacheName 配置不同的過期時間

3.1.4.1 ,自定義 CacheResolver

public class CustomCacheResolver extends SimpleCacheResolver {

    private Map<String, Caffeine<Object, Object>> caffeineConfigMap;

    public CustomCacheResolver(CacheManager cacheManager, Map<String, Caffeine<Object, Object>> caffeineConfigMap) {
        super(cacheManager);
        this.caffeineConfigMap = caffeineConfigMap;
    }

    @Override
    protected Caffeine<Object, Object> createCacheConfiguration(String cacheName) {
        return caffeineConfigMap.get(cacheName);
    }
}

 @Bean
    public CustomCacheResolver customCacheResolver(CacheManager cacheManager) {
        Map<String, Caffeine<Object, Object>> caffeineConfigMap = new HashMap<>();
        caffeineConfigMap.put("cache1", caffeineConfig1());
        caffeineConfigMap.put("cache2", caffeineConfig2());

        return new CustomCacheResolver(cacheManager, caffeineConfigMap);
    }

3.1.5 配置多個 cachemanger

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CaffeineCacheManager cacheManager1() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(caffeineConfig1());
        return cacheManager;
    }

    @Bean
    public CaffeineCacheManager cacheManager2() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(caffeineConfig2());
        return cacheManager;
    }

    @Bean
    public Caffeine<Object, Object> caffeineConfig1() {
        return Caffeine.newBuilder()
            .maximumSize(500)
            .expireAfterWrite(10, TimeUnit.MINUTES);
    }

    @Bean
    public Caffeine<Object, Object> caffeineConfig2() {
        return Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(30, TimeUnit.MINUTES);
    }
}

@Cacheable(cacheNames = "cache1", cacheManager = "cacheManager1")
public Object cache1Method() {
    // ...
}

@Cacheable(cacheNames = "cache2", cacheManager = "cacheManager2")
public Object cache2Method() {
    // ...
}

3.2 spring cache 集成redis

3.2.1 依賴

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
        .entryTtl(Duration.ofMinutes(10)); // 設(shè)置緩存過期時間

    return RedisCacheManager.builder(redisConnectionFactory)
        .cacheDefaults(cacheConfiguration)
        .build();
}

3.2.2 配置類

@Configuration
public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(10)); // 設(shè)置緩存過期時間

        return RedisCacheManager.builder(redisConnectionFactory)
            .cacheDefaults(cacheConfiguration)
            .build();
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容