前言 在开发一些配置相关的业务时,由于数据变化频率很低,很适合在查询的时候使用缓存。但对于一些复杂的数据结构,使用redis存取又觉得比较麻烦。 幸好,Spring提供了注解式的缓存方案-SpringCache。所以本章就来总结下SpringCache的用法。
注解 SpringCache借助注解来实现对数据的缓存及删除。所以,这里以每个注解为入口依次了解他们的使用。
@EnableCaching @EnableCaching注解用来开启SpringCache的缓存功能。 首先来看看他的结构:
1 2 3 4 5 6 7 8 9 10 11 12 @Target (ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)@Documented @Import (CachingConfigurationSelector.class ) public @interface EnableCaching { boolean proxyTargetClass () default false ; AdviceMode mode () default AdviceMode.PROXY ; int order () default Ordered.LOWEST_PRECEDENCE ; }
接着就开始正式的配置。 一般来说,为了实现个性化配置,我们会单独建一个配置类:
1 2 3 4 5 6 7 8 9 10 11 12 @Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager () { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default" ))); return cacheManager; } }
关于CacheManager的使用,SpringCache提供了对应各种缓存工具实现类。 例如针对redis的RedisCacheManager,还有针对encache的EhCacheCacheManager的等。 这里我们使用的是SimpleCacheManager,通常是用于测试使用,不需要太多配置。
@Cacheable @Cacheable用来新增缓存,每次会先判断有无缓存,有则取缓存,无则执行方法并将结果放入缓存 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Target ({ElementType.METHOD, ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Inherited @Documented public @interface Cacheable { @AliasFor ("cacheNames" ) String[] value() default {}; @AliasFor ("value" ) String[] cacheNames() default {}; String key () default "" ; String keyGenerator () default "" ; String cacheManager () default "" ; String cacheResolver () default "" ; String condition () default "" ; String unless () default "" ; boolean sync () default false ; }
@CachePut @CachePut用来更新缓存,每次都会执行方法并将结果放入缓存 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Target ({ElementType.METHOD, ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Inherited @Documented public @interface CachePut { @AliasFor ("cacheNames" ) String[] value() default {}; @AliasFor ("value" ) String[] cacheNames() default {}; String key () default "" ; String keyGenerator () default "" ; String cacheManager () default "" ; String cacheResolver () default "" ; String condition () default "" ; String unless () default "" ; }
@CachePut的属性和@Cacheable基本一样,除了没有异步开关属性。
@CahceEvict @CacheEvice用来清除缓存,执行方法后会将对应缓存清除掉 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Target ({ElementType.METHOD, ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Inherited @Documented public @interface CacheEvict { @AliasFor ("cacheNames" ) String[] value() default {}; @AliasFor ("value" ) String[] cacheNames() default {}; String key () default "" ; String keyGenerator () default "" ; String cacheManager () default "" ; String cacheResolver () default "" ; String condition () default "" ; boolean allEntries () default false ; boolean beforeInvocation () default false ; }
@Caching @Cacheing用作在同一方法上配置多个缓存操作 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 @Target ({ElementType.METHOD, ElementType.TYPE})@Retention (RetentionPolicy.RUNTIME)@Inherited @Documented public @interface Caching { Cacheable[] cacheable() default {}; CachePut[] put() default {}; CacheEvict[] evict() default {}; }
@CacheConfig @CacheConfig提供了公用配置的实现,避免多次重复配置 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Target (ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)@Documented public @interface CacheConfig { String[] cacheNames() default {}; String keyGenerator () default "" ; String cacheManager () default "" ; String cacheResolver () default "" ; }
使用示例 虽然SpringCache简化了对缓存的操作,但相应的也有了很多限制。
基本用法 三个缓存的操作注解分别对应查询,更新,删除操作,但更新操作需要方法也返回对应的结果集。 而更新的接口通常只会返回操作结果,成功或者失败。所以这里使用@CacheEvice直接删除缓存会更方便些。
1 2 3 4 5 6 7 8 9 10 11 @Cacheable (key = "#key" , value = "default" )public Object getInfo (String key) { return result; } @CacheEvict (key = "#key" , value = "default" )public String update (String key) { return result; }
多个CacheManager 前面有说到,我们可以配置多个不同的CacheManager,然后在每个操作注解上指定想要的CacheManager。 这里简单举一个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Primary @Bean public CacheManager cacheManager () { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default" ))); return cacheManager; } @Bean public CacheManager slaveCacheManager () { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); List<String> list = Lists.newArrayList(); list.add("salve1" ); list.add("salve2" ); cacheManager.setCacheNames(list); return cacheManager; } @Override @Cacheable (key = "#key" , cacheManager = "slaveCacheManager" ,value = "salve1" )public String cacheAbleTest (String key) { log.info("cache able:{}" , key); this .value = "cache able" ; return value; } @Override @CachePut (key = "#key" , value = "default" )public String cachePutTest (String key) { log.info("cache put:{}" , key); this .value = "cache put" ; return value; }
CacheResolver的使用 先看看默认实现的CacheResolver:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 public abstract class AbstractCacheResolver implements CacheResolver , InitializingBean { @Nullable private CacheManager cacheManager; protected AbstractCacheResolver () {} protected AbstractCacheResolver (CacheManager cacheManager) { this .cacheManager = cacheManager; } public void setCacheManager (CacheManager cacheManager) { this .cacheManager = cacheManager; } public CacheManager getCacheManager () { Assert.state(this .cacheManager != null , "No CacheManager set" ); return this .cacheManager; } @Override public void afterPropertiesSet () { Assert.notNull(this .cacheManager, "CacheManager is required" ); } @Override public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) { Collection<String> cacheNames = getCacheNames(context); if (cacheNames == null ) { return Collections.emptyList(); } Collection<Cache> result = new ArrayList<>(cacheNames.size()); for (String cacheName : cacheNames) { Cache cache = getCacheManager().getCache(cacheName); if (cache == null ) { throw new IllegalArgumentException("Cannot find cache named '" + cacheName + "' for " + context.getOperation()); } result.add(cache); } return result; } @Nullable protected abstract Collection<String> getCacheNames (CacheOperationInvocationContext<?> context) ; } public class SimpleCacheResolver extends AbstractCacheResolver { public SimpleCacheResolver () {} public SimpleCacheResolver (CacheManager cacheManager) { super (cacheManager); } @Override protected Collection<String> getCacheNames (CacheOperationInvocationContext<?> context) { return context.getOperation().getCacheNames(); } @Nullable static SimpleCacheResolver of (@Nullable CacheManager cacheManager) { return (cacheManager != null ? new SimpleCacheResolver(cacheManager) : null ); } }
从上述代码可以看出,CacheResolver的作用是管理CacheManager,可以根据特定规则选择CacheManager。 下面是根据上述例子实现的CacheResolver,根据缓存名称返回CacheManager:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class MyCacheResolver implements CacheResolver { private static final Logger logger = LoggerFactory.getLogger(MyCacheResolver.class ) ; private List<CacheManager> cacheManagerList; public MyCacheResolver (List<CacheManager> cacheManagerList) { this .cacheManagerList = cacheManagerList; } @Override public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) { logger.info("method name :{}" , context.getMethod().getName()); logger.info("args:{}" , context.getArgs()); logger.info("target:{}" , context.getTarget()); String[] values = context.getMethod().getAnnotation(Cacheable.class ).value () ; logger.info("cacheName:{}" , values); Collection<Cache> caches = Lists.newArrayList(); for (CacheManager cacheManager : cacheManagerList) { if (cacheManager.getCacheNames().contains(values[0 ])) { logger.info("cache manager:{}" , cacheManager); caches.add(cacheManager.getCache(values[0 ])); } } return caches; } } @Configuration @EnableCaching public class CacheConfig { @Primary @Bean public CacheManager cacheManager () { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("default" ))); return cacheManager; } @Bean public CacheManager slaveCacheManager () { ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager(); List<String> list = Lists.newArrayList(); list.add("salve1" ); list.add("salve2" ); cacheManager.setCacheNames(list); return cacheManager; } @Bean public CacheResolver cacheResolver () { List<CacheManager> list = Lists.newArrayList(); list.add(cacheManager()); list.add(slaveCacheManager()); return new MyCacheResolver(list); } }
总结 不得不说,SpringCache极大的简化了项目中对缓存的使用。
不过,这也意味着限制了对缓存的操作,就目前来看,SpringCache适合于获取变化较少的数据的
查询接口上,例如一些配置信息的查询等。
当然,SpringCache也提供了CacheManager和CacheResolver共开发者扩展,不过本章只是简单
展示了他们的用法,所以暂时不了解他们更多的运用技巧。
总体来说,SpringCache的优势在于快速实现,代码规范化及简化,兼容性高,扩展性都比较高。
如果不考虑对缓存生命周期设置或者一些精细操作上的需求,SpringCache必然是最好的选择。