前言    在开发一些配置相关的业务时,由于数据变化频率很低,很适合在查询的时候使用缓存。但对于一些复杂的数据结构,使用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必然是最好的选择。