Spring Cache
Spring提供一套对外一致的Cache API(Cache抽象),底层Cache的实现和Cache缓存策略由开发者自行维护。Spring Cache提供Cache、CacheManager接口来进行对Cache的抽象,提供@Cacheable、@CachePut等注解来进行对Cache的使用。
Spring Cache用于代码级别的缓存,其一般用在service层。在Dao层一般使用ORM缓存,例如Mybatis缓存。在考虑底层实现时,需要根据具体业务来选择不同的实现,简单的总结如下:
| 缓存实现 | 优势 | 劣势 |
|---|---|---|
| ConcurrentMapCache | 简单易用、不需要任何的配置 | 无缓存策略、不支持集群 |
| EhCache | 高性能、高效率、支持多种缓存配置 | 需要配置ehcache策略、不支持集群 |
| Redis | 与平台无关的缓存数据库,支持集群,有许多优势(百度一下,你就知道了) | 与Java平台无关,访问需要socket通信 |
由上可见,我们一般在非集群环境下使用EhCahe,集群环境下使用Redis、Memcached等缓存,具体百度。
Spring Cache抽象
Spring实现了以下Cache的抽象,部分Cache实现了对Spring事务的支持,即事务回滚时缓存也回滚,下面除了GuavaCache外都实现了对Spring事务的支持:
- EhCache:底层Cache由EhCache实现
- ConcurrentMapCache:底层由ConcurrentMap实现
- GuavaCache:底层Cache由Google Guava实现
- CaffeineCache:底层Cache由caffeine Cache实现
- JCacheCache:底层Cache由javax.cache.Cache实现
还有其他开源项目也提供了Spring Cache的实现,例如Spring-boot-starter-data-redis提供了Redis Cache的实现。
Spring Cache、CacheManager接口API:
1 | public interface Cache { |
Spring Cache API使用:
1 |
|
Spring Cache注解
Spring中主要基于注解+AOP来操作缓存,这样使得使用缓存比较方便但也有一定的局限性,下面是Spring Cache的几个主要注解:
- @Cacheable:该注解主要用于查询方法上,每次在调用方法时会先从缓存中去取,如果取不到才会执行方法,并把方法结果放入相应的缓存中。
- @CachePut:该注解主要用于插入、更新方法上,该方法会直接把方法调用结果放入相应的缓存中。
- @CacheEvit:该注解主要删除方法上。该方法会删除相应的缓存。
- @Caching:该注解主要用于在方法上需要多个注解,其是多个注解的组合。
- @CacheConfig:该注解在Spring 4.1后有效,配置在Class上,它为该类上的所有Cache注解提供一系列默认的配置。
上诉三个注解的参数信息如下:
- value、cacheNames:用户配置缓存名称,可配置多个。
- key:缓存的Key,可以使用Spring EL表达式配置。
- keyGenerator:缓存Key生成器,如果没有配置Key,则会自动调用生成Key。
- condition、unless:表示执行缓存行为的条件,可以使用Spring EL表达式配置。
- beforeInvocation:在@CachePut、@CacheEvit中使用,表示在方法前执行缓存行为,默认为false。
- allEntries:在@CacheEvit中使用,表示缓存中的所有Key。
在key、condition等中可以使用Spring EL表达式,可以更好的在其中写逻辑,下面是Spring Cache提供我们使用的Spring EL表达式:
| 方法名 | 位置 | 实例 | 描述 |
|---|---|---|---|
| methodName | root对象 | #root.methodName | 当前方法名 |
| method | root对象 | #root.method | 当前方法 |
| target | root对象 | #root.target | 当前调用该方法的对象 |
| args | root对象 | #root.args | 当前方法的参数数组 |
| argument name | 执行上下文 | #id | 当前方法的参数 |
| result | 执行上下文 | #result | 当前方法的返回值,当且仅当缓存在方法调用后执行才有用 |
下面是简单的使用:
1 |
|
Spring Cache注解相关的问题:
- 不能够自定义缓存值: Spring @CachePut、@Cacheable会自动把返回值放入缓存中,不能够自定义的放入想要的值。
- 动态代理带来缓存失效问题: Spring Cache使用AOP+注解进行缓存操作,如果我们使用动态代理且方法是内部调用或者非pulic方法则会出现缓存失效问题,例如:
1 | public void internalCall() { |
解决方法:使用AspectJ进行AOP处理。
Spring Cache使用
Spring项目
1 | <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> |
Spring Boot项目
Spring Boot项目需要在Configuration类上加@EnableCache注解,然后在yml文件中选择实现哪种类型的CacheManager,之后Spring Boot会自动注入相关CacheManager。
Spring Cache问题
在使用Spring Cache和Shiro时,出现了Cache注解失效问题。主要是因为Shiro在自定义Realm时会注入相关service对象使用该对象上的cache注解失效。所以在自定义Realm时注入的service加上@Lazy注解就可以使得注入到自定义Realm后于的service为subject Factory对象。代码如下:
1 |
|
Reference
http://jinnianshilongnian.iteye.com/blog/2001040
http://blog.chenfu3991.com/post/ehcache-diff-redis.html
https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/
https://stackoverflow.com/questions/21512791/spring-service-with-cacheable-methods-gets-initialized-without-cache-when-autowi