转载

Spring Cache抽象详解

缓存简介

缓存,我的理解是:让数据更接近于使用者;工作机制是:先从缓存中读取数据,如果没有再从慢速设备上读取实际数据(数据也会存入缓存);缓存什么:那些经常读取且不经常修改的数据/那些昂贵(CPU/IO)的且对于相同的请求有相同的计算结果的数据。如CPU--L1/L2--内存--磁盘就是一个典型的例子,CPU需要数据时先从L1/L2中读取,如果没有到内存中找,如果还没有会到磁盘上找。还有如用过Maven的朋友都应该知道,我们找依赖的时候,先从本机仓库找,再从本地服务器仓库找,最后到远程仓库服务器找;还有如京东的物流为什么那么快?他们在各个地都有分仓库,如果该仓库有货物那么送货的速度是非常快的。

 

缓存命中率

即从缓存中读取数据的次数 与 总读取次数的比率,命中率越高越好:

命中率 = 从缓存中读取次数 / (总读取次数[从缓存中读取次数 + 从慢速设备上读取的次数])

Miss率 = 没有从缓存中读取的次数 / (总读取次数[从缓存中读取次数 + 从慢速设备上读取的次数])

 

这是一个非常重要的监控指标,如果做缓存一定要健康这个指标来看缓存是否工作良好;

 

缓存策略

Eviction policy

移除策略,即如果缓存满了,从缓存中移除数据的策略;常见的有LFU、LRU、FIFO:

FIFO(First In First Out):先进先出算法,即先放入缓存的先被移除;

LRU(Least Recently Used):最久未使用算法,使用时间距离现在最久的那个被移除;

LFU(Least Frequently Used):最近最少使用算法,一定时间段内使用次数(频率)最少的那个被移除;

 

TTL(Time To Live )

存活期,即从缓存中创建时间点开始直到它到期的一个时间段(不管在这个时间段内有没有访问都将过期)

 

TTI(Time To Idle)

空闲期,即一个数据多久没被访问将从缓存中移除的时间。

 

 

到此,基本了解了缓存的知识,在Java中,我们一般对调用方法进行缓存控制,比如我调用"findUserById(Long id)",那么我应该在调用这个方法之前先从缓存中查找有没有,如果没有再掉该方法如从数据库加载用户,然后添加到缓存中,下次调用时将会从缓存中获取到数据。

 

自Spring 3.1起,提供了类似于@Transactional注解事务的注解Cache支持,且提供了Cache抽象;在此之前一般通过AOP实现;使用Spring Cache的好处:

提供基本的Cache抽象,方便切换各种底层Cache;

通过注解Cache可以实现类似于事务一样,缓存逻辑透明的应用到我们的业务代码上,且只需要更少的代码就可以完成;

提供事务回滚时也自动回滚缓存;

支持比较复杂的缓存逻辑;

 

对于Spring Cache抽象,主要从以下几个方面学习:

  • Cache API及默认提供的实现
  • Cache注解
  • 实现复杂的Cache逻辑

 

Cache API及默认提供的实现

Spring提供的核心Cache接口: 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=package%20org.springframework.cache%3B%0A%0Apublic%20interface%20Cache%20%7B%0A%09String%20getName()%3B%20%20%2F%2F%E7%BC%93%E5%AD%98%E7%9A%84%E5%90%8D%E5%AD%97%0A%09Object%20getNativeCache()%3B%20%2F%2F%E5%BE%97%E5%88%B0%E5%BA%95%E5%B1%82%E4%BD%BF%E7%94%A8%E7%9A%84%E7%BC%93%E5%AD%98%EF%BC%8C%E5%A6%82Ehcache%0A%09ValueWrapper%20get(Object%20key)%3B%20%2F%2F%E6%A0%B9%E6%8D%AEkey%E5%BE%97%E5%88%B0%E4%B8%80%E4%B8%AAValueWrapper%EF%BC%8C%E7%84%B6%E5%90%8E%E8%B0%83%E7%94%A8%E5%85%B6get%E6%96%B9%E6%B3%95%E8%8E%B7%E5%8F%96%E5%80%BC%0A%09%3CT%3E%20T%20get(Object%20key%2C%20Class%3CT%3E%20type)%3B%2F%2F%E6%A0%B9%E6%8D%AEkey%EF%BC%8C%E5%92%8Cvalue%E7%9A%84%E7%B1%BB%E5%9E%8B%E7%9B%B4%E6%8E%A5%E8%8E%B7%E5%8F%96value%0A%09void%20put(Object%20key%2C%20Object%20value)%3B%2F%2F%E5%BE%80%E7%BC%93%E5%AD%98%E6%94%BE%E6%95%B0%E6%8D%AE%0A%09void%20evict(Object%20key)%3B%2F%2F%E4%BB%8E%E7%BC%93%E5%AD%98%E4%B8%AD%E7%A7%BB%E9%99%A4key%E5%AF%B9%E5%BA%94%E7%9A%84%E7%BC%93%E5%AD%98%0A%09void%20clear()%3B%20%2F%2F%E6%B8%85%E7%A9%BA%E7%BC%93%E5%AD%98%0A%0A%09interface%20ValueWrapper%20%7B%20%2F%2F%E7%BC%93%E5%AD%98%E5%80%BC%E7%9A%84Wrapper%0A%09%09Object%20get()%3B%20%2F%2F%E5%BE%97%E5%88%B0%E7%9C%9F%E5%AE%9E%E7%9A%84value%0A%20%20%20%20%20%20%20%20%7D%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. package org.springframework.cache;  
  2.   
  3. public interface Cache {  
  4.     String getName();  //缓存的名字  
  5.     Object getNativeCache(); //得到底层使用的缓存,如Ehcache  
  6.     ValueWrapper get(Object key); //根据key得到一个ValueWrapper,然后调用其get方法获取值  
  7.     <T> T get(Object key, Class<T> type);//根据key,和value的类型直接获取value  
  8.     void put(Object key, Object value);//往缓存放数据  
  9.     void evict(Object key);//从缓存中移除key对应的缓存  
  10.     void clear(); //清空缓存  
  11.   
  12.     interface ValueWrapper { //缓存值的Wrapper  
  13.         Object get(); //得到真实的value  
  14.         }  
  15. }  

提供了缓存操作的读取/写入/移除方法;

 

 

默认提供了如下实现:

ConcurrentMapCache:使用java.util.concurrent.ConcurrentHashMap实现的Cache;

GuavaCache:对Guava com.google.common.cache.Cache进行的Wrapper,需要Google Guava 12.0或更高版本,@since spring 4;

EhCacheCache:使用Ehcache实现

JCacheCache:对javax.cache.Cache进行的wrapper,@since spring 3.2;spring4将此类更新到JCache 0.11版本;

 

另外,因为我们在应用中并不是使用一个Cache,而是多个,因此Spring还提供了CacheManager抽象,用于缓存的管理: 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=package%20org.springframework.cache%3B%0Aimport%20java.util.Collection%3B%0Apublic%20interface%20CacheManager%20%7B%0A%09Cache%20getCache(String%20name)%3B%20%2F%2F%E6%A0%B9%E6%8D%AECache%E5%90%8D%E5%AD%97%E8%8E%B7%E5%8F%96Cache%20%0A%09Collection%3CString%3E%20getCacheNames()%3B%20%2F%2F%E5%BE%97%E5%88%B0%E6%89%80%E6%9C%89Cache%E7%9A%84%E5%90%8D%E5%AD%97%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. package org.springframework.cache;  
  2. import java.util.Collection;  
  3. public interface CacheManager {  
  4.     Cache getCache(String name); //根据Cache名字获取Cache   
  5.     Collection<String> getCacheNames(); //得到所有Cache的名字  
  6. }  

 

默认提供的实现: 

ConcurrentMapCacheManager/ConcurrentMapCacheFactoryBean:管理ConcurrentMapCache;

GuavaCacheManager;

EhCacheCacheManager/EhCacheManagerFactoryBean;

JCacheCacheManager/JCacheManagerFactoryBean;

 

另外还提供了CompositeCacheManager用于组合CacheManager,即可以从多个CacheManager中轮询得到相应的Cache,如

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%3Cbean%20id%3D%22cacheManager%22%20class%3D%22org.springframework.cache.support.CompositeCacheManager%22%3E%0A%20%20%20%20%3Cproperty%20name%3D%22cacheManagers%22%3E%0A%20%20%20%20%20%20%20%20%3Clist%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cref%20bean%3D%22ehcacheManager%22%2F%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cref%20bean%3D%22jcacheManager%22%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2Flist%3E%0A%20%20%20%20%3C%2Fproperty%3E%0A%20%20%20%20%3Cproperty%20name%3D%22fallbackToNoOpCache%22%20value%3D%22true%22%2F%3E%0A%3C%2Fbean%3E" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. <bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">  
  2.     <property name="cacheManagers">  
  3.         <list>  
  4.             <ref bean="ehcacheManager"/>  
  5.             <ref bean="jcacheManager"/>  
  6.         </list>  
  7.     </property>  
  8.     <property name="fallbackToNoOpCache" value="true"/>  
  9. </bean>  

当我们调用cacheManager.getCache(cacheName) 时,会先从第一个cacheManager中查找有没有cacheName的cache,如果没有接着查找第二个,如果最后找不到,因为fallbackToNoOpCache=true,那么将返回一个NOP的Cache否则返回null。

 

 

除了GuavaCacheManager之外,其他Cache都支持Spring事务的,即如果事务回滚了,Cache的数据也会移除掉。

 

Spring不进行Cache的缓存策略的维护,这些都是由底层Cache自己实现,Spring只是提供了一个Wrapper,提供一套对外一致的API。

 

示例

需要添加Ehcache依赖,具体依赖轻参考pom.xml

 

SpringCacheTest.java

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Test%0A%20%20%20%20public%20void%20test()%20throws%20IOException%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%E5%88%9B%E5%BB%BA%E5%BA%95%E5%B1%82Cache%0A%20%20%20%20%20%20%20%20net.sf.ehcache.CacheManager%20ehcacheManager%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%20new%20net.sf.ehcache.CacheManager(new%20ClassPathResource(%22ehcache.xml%22).getInputStream())%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%E5%88%9B%E5%BB%BASpring%E7%9A%84CacheManager%0A%20%20%20%20%20%20%20%20EhCacheCacheManager%20cacheCacheManager%20%3D%20new%20EhCacheCacheManager()%3B%0A%20%20%20%20%20%20%20%20%2F%2F%E8%AE%BE%E7%BD%AE%E5%BA%95%E5%B1%82%E7%9A%84CacheManager%0A%20%20%20%20%20%20%20%20cacheCacheManager.setCacheManager(ehcacheManager)%3B%0A%0A%20%20%20%20%20%20%20%20Long%20id%20%3D%201L%3B%0A%20%20%20%20%20%20%20%20User%20user%20%3D%20new%20User(id%2C%20%22zhang%22%2C%20%22zhang%40gmail.com%22)%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%E6%A0%B9%E6%8D%AE%E7%BC%93%E5%AD%98%E5%90%8D%E5%AD%97%E8%8E%B7%E5%8F%96Cache%0A%20%20%20%20%20%20%20%20Cache%20cache%20%3D%20cacheCacheManager.getCache(%22user%22)%3B%0A%20%20%20%20%20%20%20%20%2F%2F%E5%BE%80%E7%BC%93%E5%AD%98%E5%86%99%E6%95%B0%E6%8D%AE%0A%20%20%20%20%20%20%20%20cache.put(id%2C%20user)%3B%0A%20%20%20%20%20%20%20%20%2F%2F%E4%BB%8E%E7%BC%93%E5%AD%98%E8%AF%BB%E6%95%B0%E6%8D%AE%0A%20%20%20%20%20%20%20%20Assert.assertNotNull(cache.get(id%2C%20User.class))%3B%0A%20%20%20%20%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Test  
  2. public void test() throws IOException {  
  3.     //创建底层Cache  
  4.     net.sf.ehcache.CacheManager ehcacheManager  
  5.             = new net.sf.ehcache.CacheManager(new ClassPathResource("ehcache.xml").getInputStream());  
  6.   
  7.     //创建Spring的CacheManager  
  8.     EhCacheCacheManager cacheCacheManager = new EhCacheCacheManager();  
  9.     //设置底层的CacheManager  
  10.     cacheCacheManager.setCacheManager(ehcacheManager);  
  11.   
  12.     Long id = 1L;  
  13.     User user = new User(id, "zhang""zhang@gmail.com");  
  14.   
  15.     //根据缓存名字获取Cache  
  16.     Cache cache = cacheCacheManager.getCache("user");  
  17.     //往缓存写数据  
  18.     cache.put(id, user);  
  19.     //从缓存读数据  
  20.     Assert.assertNotNull(cache.get(id, User.class));  
  21. }  

此处直接使用Spring提供的API进行操作;我们也可以通过xml/注解方式配置到spring容器;

 

xml风格的(spring-cache.xml):

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%3Cbean%20id%3D%22ehcacheManager%22%20class%3D%22org.springframework.cache.ehcache.EhCacheManagerFactoryBean%22%3E%0A%20%20%20%20%20%20%20%20%3Cproperty%20name%3D%22configLocation%22%20value%3D%22classpath%3Aehcache.xml%22%2F%3E%0A%20%20%20%20%3C%2Fbean%3E%0A%0A%20%20%20%20%3Cbean%20id%3D%22cacheManager%22%20class%3D%22org.springframework.cache.ehcache.EhCacheCacheManager%22%3E%0A%20%20%20%20%20%20%20%20%3Cproperty%20name%3D%22cacheManager%22%20ref%3D%22ehcacheManager%22%2F%3E%0A%20%20%20%20%20%20%20%20%3Cproperty%20name%3D%22transactionAware%22%20value%3D%22true%22%2F%3E%0A%20%20%20%20%3C%2Fbean%3E" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
  2.     <property name="configLocation" value="classpath:ehcache.xml"/>  
  3. </bean>  
  4.   
  5. <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">  
  6.     <property name="cacheManager" ref="ehcacheManager"/>  
  7.     <property name="transactionAware" value="true"/>  
  8. </bean>  

spring提供EhCacheManagerFactoryBean来简化ehcache cacheManager的创建,这样注入configLocation,会自动根据路径从classpath下找,比编码方式简单多了,然后就可以从spring容器获取cacheManager进行操作了。此处的transactionAware表示是否事务环绕的,如果true,则如果事务回滚,缓存也回滚,默认false。

 

 

注解风格的(AppConfig.java):

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Bean%0A%20%20%20%20public%20CacheManager%20cacheManager()%20%7B%0A%0A%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20net.sf.ehcache.CacheManager%20ehcacheCacheManager%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%20new%20net.sf.ehcache.CacheManager(new%20ClassPathResource(%22ehcache.xml%22).getInputStream())%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20EhCacheCacheManager%20cacheCacheManager%20%3D%20new%20EhCacheCacheManager(ehcacheCacheManager)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20cacheCacheManager%3B%0A%20%20%20%20%20%20%20%20%7D%20catch%20(IOException%20e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20RuntimeException(e)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Bean  
  2. public CacheManager cacheManager() {  
  3.   
  4.     try {  
  5.         net.sf.ehcache.CacheManager ehcacheCacheManager  
  6.                 = new net.sf.ehcache.CacheManager(new ClassPathResource("ehcache.xml").getInputStream());  
  7.   
  8.         EhCacheCacheManager cacheCacheManager = new EhCacheCacheManager(ehcacheCacheManager);  
  9.         return cacheCacheManager;  
  10.     } catch (IOException e) {  
  11.         throw new RuntimeException(e);  
  12.     }  
  13. }  

和编程方式差不多就不多介绍了。

另外,除了这些默认的Cache之外,我们可以写自己的Cache实现;而且即使不用之后的Spring Cache注解,我们也尽量使用Spring Cache API进行Cache的操作,如果要替换底层Cache也是非常方便的。到此基本的Cache API就介绍完了,接下来我们来看看使用Spring Cache注解来简化Cache的操作。

 

Cache注解

启用Cache注解

XML风格的(spring-cache.xml):

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%3Ccache%3Aannotation-driven%20cache-manager%3D%22cacheManager%22%20proxy-target-class%3D%22true%22%2F%3E" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>  

另外还可以指定一个 key-generator,即默认的key生成策略,后边讨论;

 

 

注解风格的(AppConfig.java):

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40Configuration%0A%40ComponentScan(basePackages%20%3D%20%22com.sishuok.spring.service%22)%0A%40EnableCaching(proxyTargetClass%20%3D%20true)%0Apublic%20class%20AppConfig%20implements%20CachingConfigurer%20%7B%0A%20%20%20%20%40Bean%0A%20%20%20%20%40Override%0A%20%20%20%20public%20CacheManager%20cacheManager()%20%7B%0A%0A%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20net.sf.ehcache.CacheManager%20ehcacheCacheManager%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%20new%20net.sf.ehcache.CacheManager(new%20ClassPathResource(%22ehcache.xml%22).getInputStream())%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20EhCacheCacheManager%20cacheCacheManager%20%3D%20new%20EhCacheCacheManager(ehcacheCacheManager)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20cacheCacheManager%3B%0A%20%20%20%20%20%20%20%20%7D%20catch%20(IOException%20e)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20throw%20new%20RuntimeException(e)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20%40Bean%0A%20%20%20%20%40Override%0A%20%20%20%20public%20KeyGenerator%20keyGenerator()%20%7B%0A%20%20%20%20%20%20%20%20return%20new%20SimpleKeyGenerator()%3B%0A%20%20%20%20%7D%0A%7D%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Configuration  
  2. @ComponentScan(basePackages = "com.sishuok.spring.service")  
  3. @EnableCaching(proxyTargetClass = true)  
  4. public class AppConfig implements CachingConfigurer {  
  5.     @Bean  
  6.     @Override  
  7.     public CacheManager cacheManager() {  
  8.   
  9.         try {  
  10.             net.sf.ehcache.CacheManager ehcacheCacheManager  
  11.                     = new net.sf.ehcache.CacheManager(new ClassPathResource("ehcache.xml").getInputStream());  
  12.   
  13.             EhCacheCacheManager cacheCacheManager = new EhCacheCacheManager(ehcacheCacheManager);  
  14.             return cacheCacheManager;  
  15.         } catch (IOException e) {  
  16.             throw new RuntimeException(e);  
  17.         }  
  18.     }  
  19.   
  20.     @Bean  
  21.     @Override  
  22.     public KeyGenerator keyGenerator() {  
  23.         return new SimpleKeyGenerator();  
  24.     }  
  25. }  

1、使用@EnableCaching启用Cache注解支持;

2、实现CachingConfigurer,然后注入需要的cacheManager和keyGenerator;从spring4开始默认的keyGenerator是SimpleKeyGenerator;

 

@CachePut 

应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存: 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22)%0A%20%20%20%20public%20User%20save(User%20user)%20%7B%0A%20%20%20%20%20%20%20%20users.add(user)%3B%0A%20%20%20%20%20%20%20%20return%20user%3B%0A%20%20%20%20%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CachePut(value = "user", key = "#user.id")  
  2. public User save(User user) {  
  3.     users.add(user);  
  4.     return user;  
  5. }  

即调用该方法时,会把user.id作为key,返回值作为value放入缓存;

 

@CachePut注解:

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=public%20%40interface%20CachePut%20%7B%0A%09String%5B%5D%20value()%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%E7%BC%93%E5%AD%98%E7%9A%84%E5%90%8D%E5%AD%97%EF%BC%8C%E5%8F%AF%E4%BB%A5%E6%8A%8A%E6%95%B0%E6%8D%AE%E5%86%99%E5%88%B0%E5%A4%9A%E4%B8%AA%E7%BC%93%E5%AD%98%0A%09String%20key()%20default%20%22%22%3B%20%20%20%20%20%20%20%2F%2F%E7%BC%93%E5%AD%98key%EF%BC%8C%E5%A6%82%E6%9E%9C%E4%B8%8D%E6%8C%87%E5%AE%9A%E5%B0%86%E4%BD%BF%E7%94%A8%E9%BB%98%E8%AE%A4%E7%9A%84KeyGenerator%E7%94%9F%E6%88%90%EF%BC%8C%E5%90%8E%E8%BE%B9%E4%BB%8B%E7%BB%8D%0A%09String%20condition()%20default%20%22%22%3B%20%2F%2F%E6%BB%A1%E8%B6%B3%E7%BC%93%E5%AD%98%E6%9D%A1%E4%BB%B6%E7%9A%84%E6%95%B0%E6%8D%AE%E6%89%8D%E4%BC%9A%E6%94%BE%E5%85%A5%E7%BC%93%E5%AD%98%EF%BC%8Ccondition%E5%9C%A8%E8%B0%83%E7%94%A8%E6%96%B9%E6%B3%95%E4%B9%8B%E5%89%8D%E5%92%8C%E4%B9%8B%E5%90%8E%E9%83%BD%E4%BC%9A%E5%88%A4%E6%96%AD%0A%09String%20unless()%20default%20%22%22%3B%20%20%20%20%2F%2F%E7%94%A8%E4%BA%8E%E5%90%A6%E5%86%B3%E7%BC%93%E5%AD%98%E6%9B%B4%E6%96%B0%E7%9A%84%EF%BC%8C%E4%B8%8D%E5%83%8Fcondition%EF%BC%8C%E8%AF%A5%E8%A1%A8%E8%BE%BE%E5%8F%AA%E5%9C%A8%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E4%B9%8B%E5%90%8E%E5%88%A4%E6%96%AD%EF%BC%8C%E6%AD%A4%E6%97%B6%E5%8F%AF%E4%BB%A5%E6%8B%BF%E5%88%B0%E8%BF%94%E5%9B%9E%E5%80%BCresult%E8%BF%9B%E8%A1%8C%E5%88%A4%E6%96%AD%E4%BA%86%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. public @interface CachePut {  
  2.     String[] value();              //缓存的名字,可以把数据写到多个缓存  
  3.     String key() default "";       //缓存key,如果不指定将使用默认的KeyGenerator生成,后边介绍  
  4.     String condition() default ""//满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断  
  5.     String unless() default "";    //用于否决缓存更新的,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了  
  6. }  

 

@CacheEvict 

即应用到移除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据:

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22)%20%2F%2F%E7%A7%BB%E9%99%A4%E6%8C%87%E5%AE%9Akey%E7%9A%84%E6%95%B0%E6%8D%AE%0A%20%20%20%20public%20User%20delete(User%20user)%20%7B%0A%20%20%20%20%20%20%20%20users.remove(user)%3B%0A%20%20%20%20%20%20%20%20return%20user%3B%0A%20%20%20%20%7D%0A%20%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20allEntries%20%3D%20true)%20%2F%2F%E7%A7%BB%E9%99%A4%E6%89%80%E6%9C%89%E6%95%B0%E6%8D%AE%0A%20%20%20%20public%20void%20deleteAll()%20%7B%0A%20%20%20%20%20%20%20%20users.clear()%3B%0A%20%20%20%20%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CacheEvict(value = "user", key = "#user.id"//移除指定key的数据  
  2. public User delete(User user) {  
  3.     users.remove(user);  
  4.     return user;  
  5. }  
  6. @CacheEvict(value = "user", allEntries = true//移除所有数据  
  7. public void deleteAll() {  
  8.     users.clear();  
  9. }  

 

@CacheEvict注解:

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=public%20%40interface%20CacheEvict%20%7B%0A%09String%5B%5D%20value()%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%E8%AF%B7%E5%8F%82%E8%80%83%40CachePut%0A%09String%20key()%20default%20%22%22%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%E8%AF%B7%E5%8F%82%E8%80%83%40CachePut%0A%09String%20condition()%20default%20%22%22%3B%20%20%20%20%20%20%20%20%20%20%20%2F%2F%E8%AF%B7%E5%8F%82%E8%80%83%40CachePut%0A%09boolean%20allEntries()%20default%20false%3B%20%20%20%20%20%20%2F%2F%E6%98%AF%E5%90%A6%E7%A7%BB%E9%99%A4%E6%89%80%E6%9C%89%E6%95%B0%E6%8D%AE%0A%09boolean%20beforeInvocation()%20default%20false%3B%2F%2F%E6%98%AF%E8%B0%83%E7%94%A8%E6%96%B9%E6%B3%95%E4%B9%8B%E5%89%8D%E7%A7%BB%E9%99%A4%2F%E8%BF%98%E6%98%AF%E8%B0%83%E7%94%A8%E4%B9%8B%E5%90%8E%E7%A7%BB%E9%99%A4" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. public @interface CacheEvict {  
  2.     String[] value();                        //请参考@CachePut  
  3.     String key() default "";                 //请参考@CachePut  
  4.     String condition() default "";           //请参考@CachePut  
  5.     boolean allEntries() default false;      //是否移除所有数据  
  6.     boolean beforeInvocation() default false;//是调用方法之前移除/还是调用之后移除  

 

@Cacheable

应用到读取数据的方法上,即可缓存的方法,如查找方法:先从缓存中读取,如果没有再调用方法获取数据,然后把数据添加到缓存中:

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%40Cacheable(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23id%22)%0A%20%20%20%20public%20User%20findById(final%20Long%20id)%20%7B%0A%20%20%20%20%20%20%20%20System.out.println(%22cache%20miss%2C%20invoke%20find%20by%20id%2C%20id%3A%22%20%2B%20id)%3B%0A%20%20%20%20%20%20%20%20for%20(User%20user%20%3A%20users)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(user.getId().equals(id))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20user%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Cacheable(value = "user", key = "#id")  
  2.  public User findById(final Long id) {  
  3.      System.out.println("cache miss, invoke find by id, id:" + id);  
  4.      for (User user : users) {  
  5.          if (user.getId().equals(id)) {  
  6.              return user;  
  7.          }  
  8.      }  
  9.      return null;  
  10.  }  

 

@Cacheable注解:

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=public%20%40interface%20Cacheable%20%7B%0A%09String%5B%5D%20value()%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%E8%AF%B7%E5%8F%82%E8%80%83%40CachePut%0A%09String%20key()%20default%20%22%22%3B%20%20%20%20%20%20%2F%2F%E8%AF%B7%E5%8F%82%E8%80%83%40CachePut%0A%09String%20condition()%20default%20%22%22%3B%2F%2F%E8%AF%B7%E5%8F%82%E8%80%83%40CachePut%0A%09String%20unless()%20default%20%22%22%3B%20%20%20%2F%2F%E8%AF%B7%E5%8F%82%E8%80%83%40CachePut%26nbsp%3B%26nbsp%3B" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. public @interface Cacheable {  
  2.     String[] value();             //请参考@CachePut  
  3.     String key() default "";      //请参考@CachePut  
  4.     String condition() default "";//请参考@CachePut  
  5.     String unless() default "";   //请参考@CachePut    

 

运行流程

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=1%E3%80%81%E9%A6%96%E5%85%88%E6%89%A7%E8%A1%8C%40CacheEvict%EF%BC%88%E5%A6%82%E6%9E%9CbeforeInvocation%3Dtrue%E4%B8%94condition%20%E9%80%9A%E8%BF%87%EF%BC%89%EF%BC%8C%E5%A6%82%E6%9E%9CallEntries%3Dtrue%EF%BC%8C%E5%88%99%E6%B8%85%E7%A9%BA%E6%89%80%E6%9C%89%0A2%E3%80%81%E6%8E%A5%E7%9D%80%E6%94%B6%E9%9B%86%40Cacheable%EF%BC%88%E5%A6%82%E6%9E%9Ccondition%20%E9%80%9A%E8%BF%87%EF%BC%8C%E4%B8%94key%E5%AF%B9%E5%BA%94%E7%9A%84%E6%95%B0%E6%8D%AE%E4%B8%8D%E5%9C%A8%E7%BC%93%E5%AD%98%EF%BC%89%EF%BC%8C%E6%94%BE%E5%85%A5cachePutRequests%EF%BC%88%E4%B9%9F%E5%B0%B1%E6%98%AF%E8%AF%B4%E5%A6%82%E6%9E%9CcachePutRequests%E4%B8%BA%E7%A9%BA%EF%BC%8C%E5%88%99%E6%95%B0%E6%8D%AE%E5%9C%A8%E7%BC%93%E5%AD%98%E4%B8%AD%EF%BC%89%0A3%E3%80%81%E5%A6%82%E6%9E%9CcachePutRequests%E4%B8%BA%E7%A9%BA%E4%B8%94%E6%B2%A1%E6%9C%89%40CachePut%E6%93%8D%E4%BD%9C%EF%BC%8C%E9%82%A3%E4%B9%88%E5%B0%86%E6%9F%A5%E6%89%BE%40Cacheable%E7%9A%84%E7%BC%93%E5%AD%98%EF%BC%8C%E5%90%A6%E5%88%99result%3D%E7%BC%93%E5%AD%98%E6%95%B0%E6%8D%AE%EF%BC%88%E4%B9%9F%E5%B0%B1%E6%98%AF%E8%AF%B4%E5%8F%AA%E8%A6%81%E5%BD%93%E6%B2%A1%E6%9C%89cache%20put%E8%AF%B7%E6%B1%82%E6%97%B6%E6%89%8D%E4%BC%9A%E6%9F%A5%E6%89%BE%E7%BC%93%E5%AD%98%EF%BC%89%0A4%E3%80%81%E5%A6%82%E6%9E%9C%E6%B2%A1%E6%9C%89%E6%89%BE%E5%88%B0%E7%BC%93%E5%AD%98%EF%BC%8C%E9%82%A3%E4%B9%88%E8%B0%83%E7%94%A8%E5%AE%9E%E9%99%85%E7%9A%84API%EF%BC%8C%E6%8A%8A%E7%BB%93%E6%9E%9C%E6%94%BE%E5%85%A5result%0A5%E3%80%81%E5%A6%82%E6%9E%9C%E6%9C%89%40CachePut%E6%93%8D%E4%BD%9C(%E5%A6%82%E6%9E%9Ccondition%20%E9%80%9A%E8%BF%87)%EF%BC%8C%E9%82%A3%E4%B9%88%E6%94%BE%E5%85%A5cachePutRequests%0A6%E3%80%81%E6%89%A7%E8%A1%8CcachePutRequests%EF%BC%8C%E5%B0%86%E6%95%B0%E6%8D%AE%E5%86%99%E5%85%A5%E7%BC%93%E5%AD%98%EF%BC%88unless%E4%B8%BA%E7%A9%BA%E6%88%96%E8%80%85unless%E8%A7%A3%E6%9E%90%E7%BB%93%E6%9E%9C%E4%B8%BAfalse%EF%BC%89%EF%BC%9B%0A7%E3%80%81%E6%89%A7%E8%A1%8C%40CacheEvict%EF%BC%88%E5%A6%82%E6%9E%9CbeforeInvocation%3Dfalse%20%E4%B8%94%20condition%20%E9%80%9A%E8%BF%87%EF%BC%89%EF%BC%8C%E5%A6%82%E6%9E%9CallEntries%3Dtrue%EF%BC%8C%E5%88%99%E6%B8%85%E7%A9%BA%E6%89%80%E6%9C%89" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. 1、首先执行@CacheEvict(如果beforeInvocation=true且condition 通过),如果allEntries=true,则清空所有  
  2. 2、接着收集@Cacheable(如果condition 通过,且key对应的数据不在缓存),放入cachePutRequests(也就是说如果cachePutRequests为空,则数据在缓存中)  
  3. 3、如果cachePutRequests为空且没有@CachePut操作,那么将查找@Cacheable的缓存,否则result=缓存数据(也就是说只要当没有cache put请求时才会查找缓存)  
  4. 4、如果没有找到缓存,那么调用实际的API,把结果放入result  
  5. 5、如果有@CachePut操作(如果condition 通过),那么放入cachePutRequests  
  6. 6、执行cachePutRequests,将数据写入缓存(unless为空或者unless解析结果为false);  
  7. 7、执行@CacheEvict(如果beforeInvocation=false 且 condition 通过),如果allEntries=true,则清空所有  

流程中需要注意的就是2/3/4步:

如果有@CachePut操作,即使有@Cacheable也不会从缓存中读取;问题很明显,如果要混合多个注解使用,不能组合使用@CachePut和@Cacheable;官方说应该避免这样使用(解释是如果带条件的注解相互排除的场景);不过个人感觉还是不要考虑这个好,让用户来决定如何使用,否则一会介绍的场景不能满足。

提供的SpEL上下文数据

Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:

名字位置描述示例

methodName

root对象

当前被调用的方法名

#root.methodName

method

root对象

当前被调用的方法

#root.method.name

target

root对象

当前被调用的目标对象

#root.target

targetClass

root对象

当前被调用的目标对象类

#root.targetClass

args

root对象

当前被调用的方法的参数列表

#root.args[0]

caches

root对象

当前方法调用使用的缓存列表(如@Cacheable(value={"cache1", "cache2"})),则有两个cache

#root.caches[0].name

argument name

执行上下文

当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数

#user.id

result

执行上下文

方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,'cache evict'的beforeInvocation=false)

#result

通过这些数据我们可能实现比较复杂的缓存逻辑了,后边再来介绍。

 

Key生成器

如果在Cache注解上没有指定key的话@CachePut(value = "user"),会使用KeyGenerator进行生成一个key: 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=public%20interface%20KeyGenerator%20%7B%0A%09Object%20generate(Object%20target%2C%20Method%20method%2C%20Object...%20params)%3B%0A%7D%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. public interface KeyGenerator {  
  2.     Object generate(Object target, Method method, Object... params);  
  3. }  

默认提供了DefaultKeyGenerator生成器(Spring 4之后使用SimpleKeyGenerator): 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%09%40Override%0A%09public%20Object%20generate(Object%20target%2C%20Method%20method%2C%20Object...%20params)%20%7B%0A%09%09if%20(params.length%20%3D%3D%200)%20%7B%0A%09%09%09return%20SimpleKey.EMPTY%3B%0A%09%09%7D%0A%09%09if%20(params.length%20%3D%3D%201%20%26%26%20params%5B0%5D%20!%3D%20null)%20%7B%0A%09%09%09return%20params%5B0%5D%3B%0A%09%09%7D%0A%09%09return%20new%20SimpleKey(params)%3B%0A%09%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Override  
  2. public Object generate(Object target, Method method, Object... params) {  
  3.     if (params.length == 0) {  
  4.         return SimpleKey.EMPTY;  
  5.     }  
  6.     if (params.length == 1 && params[0] != null) {  
  7.         return params[0];  
  8.     }  
  9.     return new SimpleKey(params);  
  10. }  

即如果只有一个参数,就使用参数作为key,否则使用SimpleKey作为key。

 

我们也可以自定义自己的key生成器,然后通过xml风格的<cache:annotation-driven key-generator=""/>或注解风格的CachingConfigurer中指定keyGenerator。 

 

条件缓存

根据运行流程,如下@Cacheable将在执行方法之前( #result还拿不到返回值)判断condition,如果返回true,则查缓存; 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40Cacheable(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23id%22%2C%20condition%20%3D%20%22%23id%20lt%2010%22)%0Apublic%20User%20conditionFindById(final%20Long%20id)" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Cacheable(value = "user", key = "#id", condition = "#id lt 10")  
  2. public User conditionFindById(final Long id)  

 

根据运行流程,如下@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存; 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23id%22%2C%20condition%20%3D%20%22%23result.username%20ne%20'zhang'%22)%0Apublic%20User%20conditionSave(final%20User%20user)%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")  
  2. public User conditionSave(final User user)   

  

根据运行流程,如下@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,如果返回false,则放入缓存;(即跟condition相反)

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22%2C%20unless%20%3D%20%22%23result.username%20eq%20'zhang'%22)%0A%20%20%20%20public%20User%20conditionSave2(final%20User%20user)%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")  
  2.     public User conditionSave2(final User user)   
 
根据运行流程,如下@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);且判断condition,如果返回true,则移除缓存;
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22%2C%20beforeInvocation%20%3D%20false%2C%20condition%20%3D%20%22%23result.username%20ne%20'zhang'%22)%0A%20%20%20%20public%20User%20conditionDelete(final%20User%20user)%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")  
  2. public User conditionDelete(final User user)   
 

@Caching

有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id-->user;username--->user;email--->user的缓存;此时就需要@Caching组合多个注解标签了。

 

如用户新增成功后,添加id-->user;username--->user;email--->user到缓存; 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20put%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.username%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.email%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20User%20save(User%20user)%20%7B" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.         put = {  
  3.                 @CachePut(value = "user", key = "#user.id"),  
  4.                 @CachePut(value = "user", key = "#user.username"),  
  5.                 @CachePut(value = "user", key = "#user.email")  
  6.         }  
  7. )  
  8. public User save(User user) {  

  

@Caching定义如下: 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=public%20%40interface%20Caching%20%7B%0A%09Cacheable%5B%5D%20cacheable()%20default%20%7B%7D%3B%20%2F%2F%E5%A3%B0%E6%98%8E%E5%A4%9A%E4%B8%AA%40Cacheable%0A%09CachePut%5B%5D%20put()%20default%20%7B%7D%3B%20%20%20%20%20%20%20%20%2F%2F%E5%A3%B0%E6%98%8E%E5%A4%9A%E4%B8%AA%40CachePut%0A%09CacheEvict%5B%5D%20evict()%20default%20%7B%7D%3B%20%20%20%20%2F%2F%E5%A3%B0%E6%98%8E%E5%A4%9A%E4%B8%AA%40CacheEvict%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. public @interface Caching {  
  2.     Cacheable[] cacheable() default {}; //声明多个@Cacheable  
  3.     CachePut[] put() default {};        //声明多个@CachePut  
  4.     CacheEvict[] evict() default {};    //声明多个@CacheEvict  
  5. }  

 

自定义缓存注解

比如之前的那个@Caching组合,会让方法上的注解显得整个代码比较乱,此时可以使用自定义注解把这些注解组合到一个注解中,如: 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40Caching(%0A%20%20%20%20%20%20%20%20put%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.username%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.email%22)%0A%20%20%20%20%20%20%20%20%7D%0A)%0A%40Target(%7BElementType.METHOD%2C%20ElementType.TYPE%7D)%0A%40Retention(RetentionPolicy.RUNTIME)%0A%40Inherited%0Apublic%20%40interface%20UserSaveCache%20%7B%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.         put = {  
  3.                 @CachePut(value = "user", key = "#user.id"),  
  4.                 @CachePut(value = "user", key = "#user.username"),  
  5.                 @CachePut(value = "user", key = "#user.email")  
  6.         }  
  7. )  
  8. @Target({ElementType.METHOD, ElementType.TYPE})  
  9. @Retention(RetentionPolicy.RUNTIME)  
  10. @Inherited  
  11. public @interface UserSaveCache {  
  12. }  
 

这样我们在方法上使用如下代码即可,整个代码显得比较干净。 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40UserSaveCache%0Apublic%20User%20save(User%20user)" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @UserSaveCache  
  2. public User save(User user)  
  

示例

新增/修改数据时往缓存中写 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20put%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.username%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.email%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20User%20save(User%20user)" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.         put = {  
  3.                 @CachePut(value = "user", key = "#user.id"),  
  4.                 @CachePut(value = "user", key = "#user.username"),  
  5.                 @CachePut(value = "user", key = "#user.email")  
  6.         }  
  7. )  
  8. public User save(User user)  

 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20put%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.username%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.email%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20User%20update(User%20user)%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.         put = {  
  3.                 @CachePut(value = "user", key = "#user.id"),  
  4.                 @CachePut(value = "user", key = "#user.username"),  
  5.                 @CachePut(value = "user", key = "#user.email")  
  6.         }  
  7. )  
  8. public User update(User user)   
删除数据时从缓存中移除
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20evict%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.username%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.email%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20User%20delete(User%20user)%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.         evict = {  
  3.                 @CacheEvict(value = "user", key = "#user.id"),  
  4.                 @CacheEvict(value = "user", key = "#user.username"),  
  5.                 @CacheEvict(value = "user", key = "#user.email")  
  6.         }  
  7. )  
  8. public User delete(User user)   
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20allEntries%20%3D%20true)%0A%20%20%20%20public%20void%20deleteAll()" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CacheEvict(value = "user", allEntries = true)  
  2.  public void deleteAll()  
 
查找时从缓存中读
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20cacheable%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40Cacheable(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23id%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20User%20findById(final%20Long%20id)%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.         cacheable = {  
  3.                 @Cacheable(value = "user", key = "#id")  
  4.         }  
  5. )  
  6. public User findById(final Long id)   

 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20cacheable%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40Cacheable(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23username%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20User%20findByUsername(final%20String%20username)%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.          cacheable = {  
  3.                  @Cacheable(value = "user", key = "#username")  
  4.          }  
  5.  )  
  6.  public User findByUsername(final String username)   

 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20cacheable%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40Cacheable(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23email%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20User%20findByEmail(final%20String%20email)" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.           cacheable = {  
  3.                   @Cacheable(value = "user", key = "#email")  
  4.           }  
  5.   )  
  6.   public User findByEmail(final String email)  
  

 

问题及解决方案

一、比如findByUsername时,不应该只放username-->user,应该连同id--->user和email--->user一起放入;这样下次如果按照id查找直接从缓存中就命中了;这需要根据之前的运行流程改造CacheAspectSupport: 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%2F%2F%20We%20only%20attempt%20to%20get%20a%20cached%20result%20if%20there%20are%20no%20put%20requests%0Aif%20(cachePutRequests.isEmpty()%20%26%26%20contexts.get(CachePutOperation.class).isEmpty())%20%7B%0A%20%20%20%20result%20%3D%20findCachedResult(contexts.get(CacheableOperation.class))%3B%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. // We only attempt to get a cached result if there are no put requests  
  2. if (cachePutRequests.isEmpty() && contexts.get(CachePutOperation.class).isEmpty()) {  
  3.     result = findCachedResult(contexts.get(CacheableOperation.class));  
  4. }  
改为:
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=Collection%3CCacheOperationContext%3E%20cacheOperationContexts%20%3D%20contexts.get(CacheableOperation.class)%3B%0Aif%20(!cacheOperationContexts.isEmpty())%20%7B%0A%20%20%20%20result%20%3D%20findCachedResult(cacheOperationContexts)%3B%0A%7D" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. Collection<CacheOperationContext> cacheOperationContexts = contexts.get(CacheableOperation.class);  
  2. if (!cacheOperationContexts.isEmpty()) {  
  3.     result = findCachedResult(cacheOperationContexts);  
  4. }  

然后就可以通过如下代码完成想要的功能: 

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20cacheable%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40Cacheable(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23username%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20put%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23result.id%22%2C%20condition%20%3D%20%22%23result%20!%3D%20null%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CachePut(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23result.email%22%2C%20condition%20%3D%20%22%23result%20!%3D%20null%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20User%20findByUsername(final%20String%20username)%20%7B%0A%20%20%20%20%20%20%20%20System.out.println(%22cache%20miss%2C%20invoke%20find%20by%20username%2C%20username%3A%22%20%2B%20username)%3B%0A%20%20%20%20%20%20%20%20for%20(User%20user%20%3A%20users)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(user.getUsername().equals(username))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20user%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%7D%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.         cacheable = {  
  3.                 @Cacheable(value = "user", key = "#username")  
  4.         },  
  5.         put = {  
  6.                 @CachePut(value = "user", key = "#result.id", condition = "#result != null"),  
  7.                 @CachePut(value = "user", key = "#result.email", condition = "#result != null")  
  8.         }  
  9. )  
  10. public User findByUsername(final String username) {  
  11.     System.out.println("cache miss, invoke find by username, username:" + username);  
  12.     for (User user : users) {  
  13.         if (user.getUsername().equals(username)) {  
  14.             return user;  
  15.         }  
  16.     }  
  17.     return null;  
  18. }  
  

 

二、缓存注解会让代码看上去比较乱;应该使用自定义注解把缓存注解提取出去;

 

三、往缓存放数据/移除数据是有条件的,而且条件可能很复杂,考虑使用SpEL表达式:

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22%2C%20condition%20%3D%20%22%23root.target.canCache()%20and%20%23root.caches%5B0%5D.get(%23user.id).get().username%20ne%20%23user.username%22%2C%20beforeInvocation%20%3D%20true)%0A%20%20%20%20public%20void%20conditionUpdate(User%20user)" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CacheEvict(value = "user", key = "#user.id", condition = "#root.target.canCache() and #root.caches[0].get(#user.id).get().username ne #user.username", beforeInvocation = true)  
  2. public void conditionUpdate(User user)  
 或更复杂的直接调用目标对象的方法进行操作(如只有修改了某个数据才从缓存中清除,比如菜单数据的缓存,只有修改了关键数据时才清空菜单对应的权限数据) 
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20%40Caching(%0A%20%20%20%20%20%20%20%20%20%20%20%20evict%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%40CacheEvict(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22%2C%20condition%20%3D%20%22%23root.target.canEvict(%23root.caches%5B0%5D%2C%20%23user.id%2C%20%23user.username)%22%2C%20beforeInvocation%20%3D%20true)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20public%20void%20conditionUpdate(User%20user)%20" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Caching(  
  2.         evict = {  
  3.                 @CacheEvict(value = "user", key = "#user.id", condition = "#root.target.canEvict(#root.caches[0], #user.id, #user.username)", beforeInvocation = true)  
  4.         }  
  5. )  
  6. public void conditionUpdate(User user)   
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%20%20%20%20public%20boolean%20canEvict(Cache%20userCache%2C%20Long%20id%2C%20String%20username)%20%7B%0A%20%20%20%20%20%20%20%20User%20cacheUser%20%3D%20userCache.get(id%2C%20User.class)%3B%0A%20%20%20%20%20%20%20%20if%20(cacheUser%20%3D%3D%20null)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20!cacheUser.getUsername().equals(username)%3B%0A%20%20%20%20%7D%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. public boolean canEvict(Cache userCache, Long id, String username) {  
  2.     User cacheUser = userCache.get(id, User.class);  
  3.     if (cacheUser == null) {  
  4.         return false;  
  5.     }  
  6.     return !cacheUser.getUsername().equals(username);  
  7. }  
如上方式唯一不太好的就是缓存条件判断方法也需要暴露出去;而且缓存代码和业务代码混合在一起,不优雅;因此把canEvict方法移到一个Helper静态类中就可以解决这个问题了:
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40CacheEvict(value%20%3D%20%22user%22%2C%20key%20%3D%20%22%23user.id%22%2C%20condition%20%3D%20%22T(com.sishuok.spring.service.UserCacheHelper).canEvict(%23root.caches%5B0%5D%2C%20%23user.id%2C%20%23user.username)%22%2C%20beforeInvocation%20%3D%20true)%0Apublic%20void%20conditionUpdate(User%20user)" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CacheEvict(value = "user", key = "#user.id", condition = "T(com.sishuok.spring.service.UserCacheHelper).canEvict(#root.caches[0], #user.id, #user.username)", beforeInvocation = true)  
  2. public void conditionUpdate(User user)  

 

四、其实对于:id--->user;username---->user;email--->user;更好的方式可能是:id--->user;username--->id;email--->id;保证user只存一份;如:

Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40CachePut(value%3D%22cacheName%22%2C%20key%3D%22%23user.username%22%2C%20cacheValue%3D%22%23user.username%22)%0Apublic%20void%20save(User%20user)%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @CachePut(value="cacheName", key="#user.username", cacheValue="#user.username")  
  2. public void save(User user)   
Java代码 <embed wmode="transparent" src="http://jinnianshilongnian.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" width="14" height="15" flashvars="clipboard=%40Cacheable(value%3D%22cacheName%22%2C%20ley%3D%22%23user.username%22%2C%20cacheValue%3D%22%23caches%5B0%5D.get(%23caches%5B0%5D.get(%23username).get())%22)%0Apublic%20User%20findByUsername(String%20username)" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer"> 收藏代码
  1. @Cacheable(value="cacheName", ley="#user.username", cacheValue="#caches[0].get(#caches[0].get(#username).get())")  
  2. public User findByUsername(String username)  

 

五、使用Spring3.1注解 缓存 模糊匹配Evict的问题 

缓存都是key-value风格的,模糊匹配本来就不应该是Cache要做的;而是通过自己的缓存代码实现;

 

六、spring cache的缺陷:例如有一个缓存存放 list<User>,现在你执行了一个 update(user)的方法,你一定不希望清除整个缓存而想替换掉update的元素

这个在现有的抽象上没有很好的方案,可以考虑通过condition在之前的Helper方法中解决;当然不是很优雅。

 

也就是说Spring Cache注解还不是很完美,我认为可以这样设计:

@Cacheable(cacheName = "缓存名称",key="缓存key/SpEL", value="缓存值/SpEL/不填默认返回值",  beforeCondition="方法执行之前的条件/SpEL", afterCondition="方法执行后的条件/SpEL", afterCache="缓存之后执行的逻辑/SpEL")

value也是一个SpEL,这样可以定制要缓存的数据;afterCache定制自己的缓存成功后的其他逻辑。

 

 

当然Spring Cache注解对于大多数场景够用了,如果场景复杂还是考虑使用AOP吧;如果自己实现请考虑使用Spring Cache API进行缓存抽象。

 

欢迎加入spring群134755960进行交流。

 

示例请参考我的github:

https://github.com/zhangkaitao/spring4-showcase/tree/master/spring-cache


原文链接:http://jinnianshilongnian.iteye.com/blog/2001040

正文到此结束
Loading...