《partner4java 讲述Spring入门》之:spring cache支持(spring3.1如何使用cache 缓存)
(以下内容参照自官方文档;p4jorm下载地址http://blog.csdn.net/partner4java/article/details/8629578;cache demo下载地址http://download.csdn.net/detail/partner4java/5102477)
若您只想尽快简单的使用,可以直接跳转到“第三部分:Hello World”
1、引言:
从3.1开始,spring框架提供了非侵入式的缓存支持。类似事务支持(transaction)一样简单,且通过定义注解来提供统一进行缓存支持。
2、理解缓存:
在互联网行业,一般使用的是MySQL数据库,不会像oracle一样包含了强大的缓存策略,所以往往大并发访问瓶颈在数据库访问上。
减少数据库访问,一般信息界面可以采用静态化,但是缓存无疑会是一个简单高效的方式(一般会采用OScache -- spring mvc进行了单独支持)。在统一的页面缓存背后还有一些数据是很少变化的,所以仅仅依靠页面缓存也是会照成不必要的“资源”浪费,设置一层数据缓存也是很重要的(借助ehcache或memcached,memcached互联网公司采用的比较多)。
(需要注意的是:无论何种缓存方案都会存在数据实效性问题。且,spring的cache方案还需要相同参数调用同一个方法在数据一致的情况下返回结果也应该是一致的。)
使用spring cache只需要完成两部分:
缓存声明:在方法上加上相应缓存注解和相应策略
configuration:定义缓存位置和具体保存策略
(spring cache并不是完全由spring提供,和transaction一样,只是对第三方框架进行上层封装)
第一分部:缓存声明
3、基于注解的缓存声明:
我们只需要学习四个注解:@Cacheable、@CachePut 、 @CacheEvict 和@Caching。
@Cacheable annotation:
正如其名字,@Cacheable用于添加在需高速缓存的方法上。这些方法默认会以参数为主键把返回结果存储到高速缓存中,以便在随后的调用(使用相同的参数)方法,直接返回高速缓存中的值,不需要实际执行此方法。
最简单的方式,只需要声明一个相关缓存策略的名称:
@Cacheable("books")
public Book findBook(ISBN isbn) {...}
也可以设置多个缓冲块,其中一个缓冲块命中即会返回,并会同步其他缓存块:
@Cacheable({ "books", "isbns" })
public Book findBook(ISBN isbn) {...}
默认缓存主键:
缓存是采用键值的方式存储,所以每次调用都要将相应的参数转化成一个合适的高效缓存主键。
默认的主键生成策略:
如果没有参数,返回0;
如果存在一个参数,则返回该参数实例;
如果不止一个参数,返回所有参数的哈希计算值。
也可以同时实现org.springframework.cache.KeyGenerator来定义自己特定的主键生成策略。
自定义缓存主键:
由于缓存块是通用的,所以不能简单的进行缓存主键声明,这样将导致生成的主键与业务不服或者与其他业务重复,如:
@Cacheable("books")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
看上去应该并不是所有参数对于生成缓存主键都是有意义的。
像这种情况下,允许通过key属性来指定主键生成策略,且key支持使用SpEL:
@Cacheable(value="books", key="#isbn"
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed);
@Cacheable(value="books", key="#isbn.rawNumber")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed);
@Cacheable(value="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed);
上面的key可以简单的通过SpEL调用参数的属性。
缓存条件(何时缓存):
同样可以对condition参数通过SpEL表达式,当返回true时将被缓存,若返回false会执行方法。
如下,当名字长度小于32时才会缓存:
@Cacheable(value="book", condition="#name.length < 32")
public Book findBook(String name)
SpEL使用上下文:

@CachePut annotation:
用法类似于@Cacheable,但是用于存放数据声明(如更新数据),所以每次都会执行,将执行后的结果存入缓存。
所以不建议把@CachePut and @Cacheable放在同一方法上,对于需要更新的数据我们应使用 @CachePut。
@CacheEvict annotation:
此注解对于去除无效的数据是非常重要的。@CacheEvict用于触发去除缓存中的数据。
除了和上面的注解用于标识缓存策略、主键和判断方式等外,又添加了allEntries属性,用于标识是否不仅只删除基于主键的数据:
@CacheEvict(value = "books", allEntries=true)
public void loadBooks(InputStream batch);
如上当需要清除一个“区域”的所有数据,而不是只清除一条数据,所以即使指定主键也是无用的。
初次之外还提供了一个beforeInvocation属性,用于表示是在执行前清除还是之后。这个属性是很有用的,比如当你在声明一个更新缓存方法之上(结合@Cacheable的场景)。
If the method does not execute (as it might be cached) or an exception is thrown, the eviction does not occur.
The latter (beforeInvocation=true) causes the eviction to occur always, before the method is invoked - this is useful in cases where the eviction does not need to be tied to the method outcome.
但是当无返回值(void)时,结合Cacheable将没有什么意义。
It is important to note that void methods can be used with @CacheEvict - as the methods act as triggers, the return values are ignored (as they don't interact with the cache) - this is not the case with @Cacheable which adds/update data into the cache and thus requires a result.
@Caching annotation:
有时我们需要添加多个注解,可以通过此注解嵌套在一起。
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key =
"#p0") })
public Book importBooks(String deposit, Date date);
自定义注解:
有时缓存的一些注解配置是常用重复的,为了避免来回拷贝,你可以自定义自己的注解,如:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Cacheable(value=“books”, key="#isbn")
public @interface SlowService {
}
然后你就可以通过你自定义的@SlowService注解替代下面的做法:
@Cacheable(value="books", key="#isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
替代方式:
@SlowService
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
(这样Spring就会自动识别,当然前提是你已经开启了缓存注解支持)
学习了,注解如何使用,那么如何通知Spring扫描相关注解呢?
缓存注解开启开关:
有时,你需要一个统一的开关进行控制缓存的开启和关闭。
只需要在你标注了@Configuration注解的类上添加@EnableCaching注解即可。
@Configuration
@EnableCaching
public class AppConfig {
}
或者通过XML方式配置使用的缓存:annotation-driven
cache:advice配置中添加了对bookService的缓存策略,缓存策略名为books,对方法findBook进行了缓存,主键为#isbn。还包括清除缓存的声明。
aop:advisor通过AspectJ的方式进行了通知。
方法和transaction非常相似,可参考相关文章。
通过XML方式有一些好处:对已有代码没有任何侵入;便于统一指定范围内的类等
第三部分:Hello World
(首先本demo采用:spring3.1.2.RELEASE + struts2.3.8 + hibernate4.1.10.Final;访问类似地址http://localhost:8080/cache/;CURD操作采用了本博主的P4jORM框架,请参照相关文章;先跑起来项目添加一些数据熟悉一下工程)
Demo1:在“spring_cache_demo没添加cache之前原始项目.zip”基础之上,通过简单的三步,来实现第一个cache功能 -- 缓存根据id获取数据方法
第一步:添加缓存注解
<!-- 不能使用2.5:否则会报错误“1. Use one of the CacheManager.create() static” --><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache-core</artifactId><version>2.4.7</version></dependency>