Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者OSCache),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。
缓存的注解有以下三个
@Cancheable@CancheEvict @CachePut
package cacheOfAnno; importorg.springframework.cache.annotation.CacheEvict; importorg.springframework.cache.annotation.Cacheable; public class AccountService { @Cacheable(value="accountCache")// 使用了一个缓存名叫 accountCache public Account getAccountByName(StringuserName) { // 方法内部实现不考虑缓存逻辑,直接实现业务 System.out.println("real queryaccount."+userName); return getFromDB(userName); } private Account getFromDB(String acctName) { System.out.println("real queryingdb..."+acctName); return new Account(acctName); } }
注意,此类的getAccountByName 方法上有一个注释 annotation,即 @Cacheable(value=”accountCache”),这个注释的意思是,当调用这个方法的时候,会从一个名叫 accountCache 的缓存中查询,如果没有,则执行实际的方法(即查询数据库),并将执行的结果存入缓存中,否则返回缓存中的对象。这里的缓存中的 key 就是参数userName,value 就是Account 对象。“accountCache”缓存是在 spring*.xml 中定义的名称。
<beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven /> <bean id="accountServiceBean"class="cacheOfAnno.AccountService"/> <!-- generic cache manager --> <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager"> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default" /> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="accountCache"/> </set> </property> </bean> </beans>
有了缓存的实现,我们还得清空缓存
为了加入清空缓存的逻辑,我们只要对 AccountService.java 进行修改,从业务逻辑的角度上看,它有两个需要清空缓存的地方
当外部调用更新了账号,则我们需要更新此账号对应的缓存
当外部调用说明重新加载,则我们需要清空所有缓存
package cacheOfAnno; importorg.springframework.cache.annotation.CacheEvict; importorg.springframework.cache.annotation.Cacheable; public class AccountService { @Cacheable(value="accountCache")//使用了一个缓存名叫 accountCache public Account getAccountByName(StringuserName) { // 方法内部实现不考虑缓存逻辑,直接实现业务 return getFromDB(userName); } @CacheEvict(value="accountCache",key="#account.getName()")//清空 accountCache 缓存 public void updateAccount(Account account) { updateDB(account); } @CacheEvict(value="accountCache",allEntries=true)// 清空 accountCache 缓存 public void reload() { } private Account getFromDB(String acctName) { System.out.println("real queryingdb..."+acctName); return new Account(acctName); } private void updateDB(Account account) { System.out.println("real updatedb..."+account.getName()); } }
前面介绍的缓存方法,没有任何条件,即所有对 accountService 对象的 getAccountByName方法的调用都会起动缓存效果,不管参数是什么值,如果有一个需求,就是只有账号名称的长度小于等于 4 的情况下,才做缓存,大于 4 的不使用缓存,那怎么实现呢?
Springcache 提供了一个很好的方法,那就是基于 SpEL 表达式的 condition 定义,这个condition 是 @Cacheable 注释的一个属性,下面我来演示一下
AccountService.java(getAccountByName方法修订,支持条件) @Cacheable(value="accountCache",condition="#userName.length()<= 4")// 缓存名叫 accountCache public Account getAccountByName(StringuserName) { // 方法内部实现不考虑缓存逻辑,直接实现业务 return getFromDB(userName); }
其中的 condition=”#userName.length() <=4”,这里使用了 SpEL 表达式访问了参数 userName对象的 length() 方法,条件表达式返回一个布尔值,true/false,当条件为 true,则进行缓存操作,否则直接调用方法执行的返回结果
注解驱动能极大减少我们编写常见缓存代码量,通过少量标签和配置文件,可达到代码具备缓存的效果,灵活性很好。