springboot入门13 – 多CacheManager应用
概述
之前有写过springboot缓存应用的说明(《 springboot入门01 – 缓存的使用
》)。不过实际的场景有时候会比较复杂一些,比如:需要同时使用redis和caffine来做多级缓存,或者需要在通用配置外应用一些个性化的配置。使用多个
CacheManager
来分别管理不同的缓存是应对这种问题的一个常规方案。
接下来介绍下如何实现多
CacheManager
应用。
多
CacheManager
应用
这里会通过一个具体的案例来进行演示。需求大致是这样的:在应用中的大部分场景都使通用的缓存配置,但是部分特殊场景需要做个性化的配置。在接下来演示中我们主要会用到Caffeine缓存。
配置
先修改下配置。这次如果继续使用SpringBoot的自启动
CacheManager
会有一些不太好控制的地方,因此不宜再使用默认的缓存配置,需要做些独立配置:
caching: spec: initialCapacity=1048576,maximumSize=1073741824,expireAfterAccess=10m special: worker: initialCapacity=32,maximumSize=128,expireAfterWrite=30s
其中 caching.spec
表示通用的缓存配置, caching.special
则表示一组需要做个性化配置的特例。
通用配置是一行字符串,读取的时候可以直接使用
@
Value
注解,个性化配置则是一个 Map
结构,读取的时候需要用到
@
ConfigurationProperties
注解,大致如下:
@Value("${caching.spec}") private String commonSpec; @Bean @ConfigurationProperties("caching.special") public Map getSpecialCase() { return new HashMap(4); }
(关于配置文件读取可以参考之前的一篇旧文:《 springboot入门07 – 配置文件详解
》)
创建
CacheManager
接下来就是根据配置文件创建
CacheManager
了。
创建通用
CacheManager
直接使用
CaffeineCacheManager
就可以了:
@Bean @Primary public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); if (isNotBlank(this.commonSpec)) { cacheManager.setCacheSpecification(this.commonSpec); } return cacheManager; }
注意
@
Primary
注解,这个注解表示没有特意注明时,优先选择这个
CacheManager
。
管理个例的
CacheManager
略有些麻烦,这里使用了
SimpleCacheManager
,代码如下:
@Bean("special") public CacheManager specialCacheManager(Map cases) { SimpleCacheManager manager = new SimpleCacheManager(); if (cases != null) { List caches = cases.entrySet().stream() .map(e -> buildCaffeineCache(e.getKey(), e.getValue())) .collect(Collectors.toList()); manager.setCaches(caches); } return manager; } private CaffeineCache buildCaffeineCache(String name, String spec) { logger.info("Cache {} specified with config:{}", name, spec); final Caffeine caffeineBuilder = Caffeine.from(spec); return new CaffeineCache(name, caffeineBuilder.build()); }
在使用这个
CacheManager
时还需要记得下在
@
CacheConfig
或
@
Cacheable
注解中注明对应的qualifier:
@CacheConfig(cacheNames = "worker", cacheManager = "special")
至此,多缓存配置已经是没有问题了。详细代码可以参考Git: zhyea / multi-cache
这里区分通用
CacheManager
与个例
CacheManager
主要依赖
@
Primary
注解实现。接下来会介绍一些其他的方案。
继承
CachingConfigurerSupport
继承抽象类
CachingConfigurerSupport
后,可以通过实现(重写)
cacheManager
(
)
方法指明默认的
CacheManager
。嗯,也就是说,节省了一个
@
Bean
和一个
@
Primary
注解。代码如下:
@Configuration public class CachingConfigSupport extends CachingConfigurerSupport { private static final Logger logger = LoggerFactory.getLogger(CachingConfigSupport.class); @Value("${caching.spec}") private String commonSpec; @Bean @ConfigurationProperties("caching.special") public Map getSpecialCase() { return new HashMap(4); } @Override public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); if (isNotBlank(this.commonSpec)) { cacheManager.setCacheSpecification(this.commonSpec); } return cacheManager; } @Bean("special") public CacheManager specialCacheManager(Map cases) { SimpleCacheManager manager = new SimpleCacheManager(); if (cases != null) { List caches = cases.entrySet().stream() .map(e -> buildCaffeineCache(e.getKey(), e.getValue())) .collect(Collectors.toList()); manager.setCaches(caches); } return manager; } private CaffeineCache buildCaffeineCache(String name, String spec) { logger.info("Cache {} specified with config:{}", name, spec); final Caffeine caffeineBuilder = Caffeine.from(spec); return new CaffeineCache(name, caffeineBuilder.build()); } }
管理个例的
CacheManager
还是需要
@
Bean
注解并设置Qualifier的。
实现
CacheResolver
这个方案,怎么说呢,应该是可操作性最强大的。如果场景再复杂些,完全可以考虑用这个方案来处理。但是就我们当前这个case来说,用
CacheResolver
来实现应该是最繁琐的了。太繁琐了,懒得写了。
姑且写一个简单的例子来演示下
CacheResolver
是怎么发挥作用的吧。
CacheResolver
的实现类如下:
@Component("multi") public class MultiCacheResolver implements CacheResolver, InitializingBean { @Value("${caching.spec}") private String commonSpec; private CacheManager manager; @Override public Collection resolveCaches(CacheOperationInvocationContext context) { Collection caches = new LinkedList(); Set cacheNames = context.getOperation().getCacheNames(); for (String name : cacheNames) { caches.add(manager.getCache(name)); } return caches; } public CacheManager newCacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); if (isNotBlank(this.commonSpec)) { cacheManager.setCacheSpecification(this.commonSpec); } return cacheManager; } @Override public void afterPropertiesSet() throws Exception { manager = newCacheManager(); } }
在代码中可以看到,是通过
resolveCaches
(
)
方法决定了提供哪些缓存。
使用
CacheResolver
后就有机会可以考虑不注入
CacheManager
的实例到容器中了,因为
CacheResolver
会管理会用到的
CacheManager
的实例。 不过在应用缓存注解的情况下,要记得指定使用哪个
CacheResolver
,像这样:
@CacheConfig(cacheNames = "worker", cacheResolver = "multi")
就这样了。示例代码都放在了这里: zhyea / multi-cache