spring中使用缓存
一、启用对缓存的支持
Spring 对缓存的支持最简单的方式就是在方法上添加@Cacheable和@CacheEvict注解,
再添加注解之前,必须先启用spring对注解驱动的支持,基于java的配置的话,直接在某个java配置类上添加@EnableCaching。
如下:
1 @Configuration 2 //启用缓存 3 @EnableCaching 4 public class CacheConfig { 5 @Bean 6 public CacheManager cacheManager(){ 7 // 配置缓存管理器 8 return new ConcurrentMapCacheManager(); 9 } 10 }
@EnableCaching会创建一个切面,并触发spring缓存注解的切点。
二、缓存管理器
在上面的代码中除了启用缓存支持外,还声明了一个缓存管理器,缓存管理器是spring缓存抽象的核心,能与多个流行的缓存实现集成。
spring提供了一些缓存管理器,主要有以下几个:
- SimpleCacheManager
- NoOpCacheManager
- ConCurrentMapCacheManager
- CompositeCacheManager
- EnCacheCacheManager
- RedisCacheManager
- GemfireCacheManager
三、使用Ehcache
引入依赖:
<dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.10.3</version> </dependency>
java配置如下:
@Bean public EhCacheCacheManager ehCacheCacheManager(CacheManager cacheManager){ return new EhCacheCacheManager(cacheManager); } @Bean public EhCacheManagerFactoryBean ehCache(){ EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean(); bean.setConfigLocation(new ClassPathResource("ehcache.xml"));
return bean;
}
需要注意的是,因为spring和ehcache都定义了CacheManager类型,为了将ehcache的CacheManager注入到spring的EhCacheCacheManager中,spring提供了EhCacheManagerFactoryBean来生成EhCache的CacheManager
除了在spring中配置bean以外,还需要针对对ehcache的配置,ehcache配置采用XML,一个简单的配置如下,指明了最大堆大小为50M,存活时间为200s,
<?xml version="1.0" encoding="utf-8" ?> <ehcache> <cache name="simpleCache" maxBytesLocalHeap="50m" timeToLiveSeconds="200"/> </ehcache>
当然这只是一个很简单的版本,有关ehcache的详细使用方法见ehcache官网。
四、使用redis缓存
spring data redis提供了RedisCacheManager,RedisCacheManager会通过RedisTemplate与redis服务器进行协作,将缓存条目存储到redis中。
为了使用RedisCacheManager需要RedisTemplate Bean以及RedisConnectionFactory实现类的bean,关于如何配置RedisTemplate,戳这。
RedisCacheManager的java配置如下:
1 @Bean 2 public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory){ 3 return RedisCacheManager.create(connectionFactory); 4 }
五、使用多个缓存管理器
可以CompositeCacheManager来聚合多个cacheManager,它会迭代这些缓存管理器,查找之前的缓存的值。
CompositeCacheManager通过一个或多个缓存管理器来进行配置,如下,同时配置了RedisCacheManager和EhCacheCacheManager
@EnableCaching @Configuration public class Config { @Bean public EhCacheManagerFactoryBean ehCache(){ EhCacheManagerFactoryBean bean = new EhCacheManagerFactoryBean(); bean.setConfigLocation(new ClassPathResource("ehcache.xml")); return bean; } @Bean public CacheManager cacheManager(net.sf.ehcache.CacheManager cacheManager, RedisConnectionFactory factory){ CompositeCacheManager compositeCacheManager = new CompositeCacheManager(); List<CacheManager> cacheManagerList = new ArrayList<>(); cacheManagerList.add(new EhCacheCacheManager(cacheManager)); cacheManagerList.add(RedisCacheManager.create(factory)); compositeCacheManager.setCacheManagers(cacheManagerList); return compositeCacheManager; } }
六、声明式注解缓存
对于缓存声明,Spring的缓存提供了一组Java注释:
-
@Cacheable
:用于缓存数据.spring在方法调用之前,首先会在缓存中查找方法的返回值,如果找到,直接返回缓存的值,否则执行方法,将返回的结果会被放到缓存中。 -
@CacheEvict
:移除缓存。spring会在缓存中移除一个活多个值。 -
@CachePut
:用于更新缓存。将方法执行的返回值放到缓存中,在方法调用前并不会检查缓存。 -
@Caching:用于组合一组缓存注解。组合一系列缓存注解以便同时使用。
-
@CacheConfig
:在类级别设置一些常见的缓存设置。
1、@Cacheable与@CachePut
方法级注解,将方法的返回值缓存起来,在下次使用相同参数时会使用缓存中的数据而不必执行该方法,最简的形式如下:
1 @Cacheable(value = "user",key = "#id") 2 public User findById(Integer id){ 3 if(id.equals(10)){ 4 System.out.println("running......"); 5 return new User(10,"jack"); 6 } 7 System.out.println("running2......"); 8 return new User(new Random().nextInt(),"tom"); 9 }
除了上面最简单的形式外@Cacheable与@CachePut还有一些可选的属性,如下,
属性 | 类型 | 描述 |
value | String[] | 要使用的缓存名称 |
condition | String | SpEL表达式,若值为false,不会讲缓存应用到方法上 |
key | String | SpEL表达式,用于计算自定义的缓存key |
unless | String | SpEL表达式,若为true,返回值不会被缓存。 |
@Cacheable还可以放到接口中的方法声明上,这样的话所有对该方法的实现都会应用相同的缓存规则。
2、@CacheEvict
该注解从缓存中移除内容,与@Cacheable和@CachePut不同,@CacheEvict可用于返回类型为void。示例如下:
1 @CacheEvict(value = "user",key = "#id") 2 public void remove(Integer id){ 3 System.out.println("remove a user..."); 4 }
当调用此方法时,会从缓存中移除一个元素,被删除条目的key与传进来的key值相同(此处使用SpEL指定)。
@CacheEvict的属性有以下几项:
属性 | 类型 | 描述 |
value | String[] | 要使用的缓存名称 |
key | String | SpEL表达式,用于计算自定义的缓存key |
condition | String | SpEL表达式,若值为false,不会讲缓存应用到方法上 |
allEntries | boolean | 若为true,特定缓存的所有条目都会被移除掉 |
beforeInvocation | boolean | 若为true,在方法调用之前移除条目,false,在方法成功调用之后再移除条目 |
3、@Caching
@Caching
允许多个嵌套 @Cacheable
,@CachePut
和@CacheEvict
注解相同的方法来使用,示例如下:
@Caching(evict = { @CacheEvict("primary"),@CacheEvict(cacheNames="secondary", key="#user.id")}) public User modify(User user){ user.setUsername("admin"); return user; }
不同的缓存注解使用不同的缓存名称或者不同的条件表达式加以区分。
4、@CacheConfig
@CacheConfig
是一个类级注释,允许共享缓存名称,自定义 KeyGenerator
,自定义CacheManager
和自定义CacheResolver
。将此批注放在类上不会打开任何缓存操作。
如下,设置了该类下所有方法缓存的名称为user:
@Service @CacheConfig(cacheNames = "user") public class UserService { ............ }
方法级别指定的配置始终会覆盖类级的@CacheConfig
。因此,这为每个缓存操作提供了三个级别的自定义:
-
-
全局,可用于
CacheManager
,KeyGenerator
。 -
类级,使用
@CacheConfig
。 -
方法级。
-
5、自定义缓存key
@Cacheable、@CachePut以及@CacheEvict都有一个名为key的属性,这个属性能够替换默认的key,他通过一个SpEL计算得来,一般都是所定义的表达式与存储在缓存中的值有关,根据此计算得到key。
在为缓存编写的SpEL中,spring暴露了一些很有用的元数据。
表达式 | 描述 |
#root.args | 传递给缓存方法的参数,形式为数组 |
#root.caches | 该方法执行时所对应的缓存,形式为数组 |
#root.target | 目标对象 |
#root.targetClass | 目标对象的类 |
#root.method | 缓存的方法 |
#root.methodName | 缓存方法的名称 |
#result | 方法调用的返回值(不能在@Cacheable上) |
#Argument | 任意的方法参数名(如argName)或参数索引(#a0,#p0) |
6、条件化缓存
@Cacheable、@CachePut均提供了两个属性实现条件化缓存:unless、condition,这两个属性都接受一个SpEL表达式。若unless属性的SpEL属性计算为true缓存将不会放到缓存中,condition属性的SpEL计算若为false,那么该方法的缓存会被禁掉。
两者的差别:
unless只能阻止将对象放进缓存,在方法调用的时候依然回去缓存中查找,如找到,则返回找到的值。unless可以引用#result
condition的表达式计算结果为false,那么在这个方法调用的过程中,缓存是被禁用的。condition不能引用#unless
示例如下:
@Cacheable(key = "#id",unless = "#result.id==10",condition = "#id>=23") public User findOne(Integer id){ if(id.equals(10)){ System.out.println("findOne......"); return new User(10,"jack"); } System.out.println("findOne......"); return new User(new Random().nextInt(),"tom"); }
当传入的Id为10时,不会将结果放到缓存中,当Id大于23时会直接禁用缓存。