设置默认的缓存管理器(CacheManager)
缓存管理器,为Spring抽象出来管理缓存的。若我们没有手动注册过CacheManager这个Bean,那么Boot容器会自动给我们注册一个。
@Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {} //注意:它执行的条件都为@ConditionalOnMissingBean(CacheManager.class),都必须为在容器中没有发现Bean,才会自动自动注册哟
然后,当我们一个项目中使用了多种缓存的时候(比如Redis、Ehcache、Caffeine等),并且自己注册了多个CacheManager的时候,并且我们开启了缓存注解@EnableCaching,我们就需要配置默认的缓存管理器了,否则就会启动失败~~
Spring内置了一些常用的缓存管理器的支持:
这个时候如果我们在上面Redis的基础上,再导入EhCache:
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.5</version> </dependency>
小Tips:Spring现在默认支持的ehcache版本为2.x版,3.x版本不支持。若要使用3.x版本进行集成,请参考相关博文。spring-boot-starter-cache
此组件能提供支持
这个时候我们配置类如下:
@Configuration @EnableCaching public class CacheConfig { @Bean public EhCacheCacheManager ehCacheManager() { EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager(); return ehCacheCacheManager; } //备注:这是boot2.x的配置。1.x的配置可以直接new即可 会少很多代码 //RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); @Bean public RedisCacheManager redisCacheManager(RedisConnectionFactory factory, ResourceLoader resourceLoader) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); // 生成一个默认配置,通过config对象即可对缓存进行自定义配置 config = config.entryTtl(Duration.ofMinutes(1)) // 设置缓存的默认过期时间,也是使用Duration设置 .disableCachingNullValues(); // 不缓存空值 // 设置一个初始化的缓存空间set集合 Set<String> cacheNames = new HashSet<>(); cacheNames.add("my-redis-cache1"); cacheNames.add("my-redis-cache2"); // 对每个缓存空间应用不同的配置 Map<String, RedisCacheConfiguration> configMap = new HashMap<>(); configMap.put("my-redis-cache1", config); configMap.put("my-redis-cache2", config.entryTtl(Duration.ofSeconds(120))); RedisCacheManager cacheManager = RedisCacheManager.builder(factory) // 使用自定义的缓存配置初始化一个cacheManager .initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置 .withInitialCacheConfigurations(configMap) .build(); return cacheManager; } }
报错:
java.lang.IllegalStateException: No CacheResolver specified, and no unique bean of type CacheManager found. Mark one as primary or declare a specific CacheManager to use. at org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:223) ~[spring-context-5.1.2.RELEASE.jar:5.1.2.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:863) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
源码看原因,其实很简单。就是要为缓存注解分配一个默认的缓存管理器,如果你给定两个,我肯定就报错了嘛
@Override public void afterSingletonsInstantiated() { if (getCacheResolver() == null) { // Lazily initialize cache resolver via default cache manager... Assert.state(this.beanFactory != null, "CacheResolver or BeanFactory must be set on cache aspect"); try { setCacheManager(this.beanFactory.getBean(CacheManager.class)); } catch (NoUniqueBeanDefinitionException ex) {}
解决方案:在你希望的默认缓存管理器上加是上@Primary注解即可(一般都标注在RedisCacheManager上面)
这样,我们就实现了同时使用多个缓存的情况,可以和谐共处了。
设置缓存的过期时间(通过缓存管理器统一设置)
这个需求经常遇到,最灵活的肯定是使用RedisTemplate的expire方法进行设置。而本处再介绍一个全局方法(也适用于缓存注解),来管理一些频繁使用的key的过期时间。
CacheManager功能其实很简单就是管理cache,接口只有两个方法,根据容器名称获取一个Cache。还有就是返回所有的缓存名称。
//根据名称获取一个Cache(在实现类里面是如果有这个Cache就返回,没有就新建一个Cache放到Map容器中) Cache getCache(String name); // 返回所有的缓存名称 Collection<String> getCacheNames();
关于自定义拦截,让缓存注解也支持过期时间的书写,可以提供思路:打断点跟踪拦截器:org.springframework.cache.interceptor.CacheInterceptor来分析
为了方便,本文以Boot1.x为例:
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); //设置默认的过期时间(不设置缓存不过期) 单位:秒 cacheManager.setDefaultExpiration(3600L); //针对具体的key 设置过期时间 所以我们完全可以定制化 //备注:此过期时间没更新一次,都会跟新成最新的值的过期时间的 Map<String, Long> expires = new HashMap<String, Long>(); expires.put("news", 60L); redisCacheManager.setExpires(expires); //是否启用前缀 默认为false cacheManager.setUsePrefix(true);
下一篇博文,我会重点分析RedisTemplate的六大序列化方式,以及使用时候我们常见的坑(有的是巨坑)
【小家Spring】RedisTemplate的序列化方式大解读,含FastJsonRedisSerializer、Genericjackson2jsonredisserializer序列化的坑