Spring对缓存的支持有两种方式:注解驱动的缓存;XML声明的缓存。
使用Spring的缓存抽象时,最为通用的方式就是在方法上添加@Cacheable和@CacheEvict注解。
通过注解配置缓存
@Configuration @EnableCaching public class CachingConfig{ @Bean public CacheManager cacheManager(){ return new ConcurrentMapCacheManager(); } }
通过XML配置缓存
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" 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.xds"> <cache:annontation-driven /> <bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager"/> </beans>
@EnableCaching和<cache:annontation-driven>工作方式是相同的。它们会创建一个切面并触发Spring缓存注解的切点。根据所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值。
Spring3.1内置了五个缓存管理器的实现:SimpleCacheManager、NoOpCacheManager、ConcurrentMapCacheManager、CompositeCacheManager和EnCacheCacheManager。
Spring Data提供了两个缓存管理器:RedisCacheManager和GemfireCacheManager
Ehcache缓存
@Configuration @EnableCaching public class CachingConfig{ @Bean public EhCacheCacheManager cacheManager(CacheManager cm){ return new EhCacheCacheManager(cm); } @Bean public EhCacheManagerFactoryBean ehcache(){ EhCacheManagerFactoryBean ehCacheFactoryBean = new EhCachaManagerFactoryBean(); ehCacheFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml")); return ehCacheFactoryBean; } }
cacheManager()方法创建了一个EhCacheManager的实例,通过传入Ehcache CacheManager实例实现的。EhCache的CacheManager要被注入到Spring的EhCacheCacheManager之中。Spring提供EhCacheManagerFactoryBean来生成EhCache的CacheManager。方法ehcache()会创建并返回一个EhCacheManagerFactoryBean实例。
EhCache为XML定义了自己的配置模式。
<ehcache> <cache name="cachename" maxBytesLocalHelp="50m" timeToLiveSeconds="100" /> </ehcache>
使用Redis缓存
Redis可以用来为Spring缓存抽象机制存储缓存条目,Spring Data Redis提供了RedisCacheManager,这是CacheManager的一个实现。RedisCacheManager会与一个Redis服务器协作,并通过RedisTemplate将缓存条目存储到Redis中。
@Configuration @EnableCaching public class CachingConfig{ @Bean public CacheManager cacheManager(RedisTemplate redisTemplate){ return new RedisCacheManager(redisTemplate); } @Bean public JedisConnectionFactory redisConnectionFactory(){ JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); jedisConnectionFactory.afterPropertiesSet(); return jedisConnectionFactory; } @Bean public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisCF){ RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>(); redisTemplate.setConnectionFactory(redisCF); redisTemplate.afterPropertiesSet(); return redisTemplate; } }
CompositeCacheManager
CompositeCacheManager要通过一个或多个缓存管理器来进行配置,它会迭代这些缓存管理器,以查找之前所缓存的值。
@Bean public CacheManager cacheManager(net.sf.ehcache.CacheManager cm. javax.cache.CacheManager jcm){ CompositeCacheManager cacheManager = new CompositeCacheManager(); List<CacheManager> managers = new ArrayList<CacheManager>(); managers.add(new JCacheCacheManager(jcm)); managers.add(new EhCacheCacheManager(cm)); managers.add(new RedisCacheManager(redisTemplate())); cacheManager.setCacheManagers(managers); return cacheManager; }
当查找缓存条目时,CompositeCacheManager首先会从JCacheCacheManager开始检查JCache实现,然后通过EhCacheCacheManager检查Ehcache,最后使用RedisCacheManager来检查Redis完成缓存条目的查找。
Spring提供了四个注解来声明缓存规则。这四个注解可以运用在方法或类上。当将其放在单个方法上时,注解所描述的缓存行为只会运用到这个方法上。如果注解放到类几倍时,缓存行为就会应用到这个类的所以方法上。
@Cacheable 表明Spring在调用方法之前,首先应该在缓存中查找方法的返回值。
@CachePut 表明Spring应该将方法的返回值放到缓存中。在方法的调用前并不会检查缓存,方法使用都会被调用
@CacheEvict 表明Srping应该在缓存中清楚一个或多个条目
@Caching 这是一个分组的注解,能够同时应用多个其他的缓存注解
@Cacheable和@CachePut都可以填充缓存。它们有一些共同属性:value(String[])要使用的缓存的名称;condition(String)SpEL表达式,如果值是false,不会将缓存应用到方法调用之上;key(String)SpEL表达式,用来计算自定义的缓存key;unless(String)SpEL表达式,如果得到的值是true,返回值不会放到缓存之中
@Cacheable("user") public User findOne(long id){ try{ return jdbcTemplate.queryForObject(SELECT_USER_BY_ID, new User(), id); }catch(EmptyResultDataAccessException e){ return null; } }
自定义缓存Key
@Cacheable和@CachePut可以用key属性替换默认的key,他是通过一个SpEL表达式计算得到的。
Spring提供了多个用来定义缓存规则的SpEL扩展
#root.args 传递给缓存方法的参数,形式为数组
#root.caches 该方法执行时所对应的缓存,形式为数组
#root.target 目标对象
#root.targetClass 目标对象的类,是#root.target.class的简写形式
#root.method 缓存方法
#root.methodName 缓存方法的名字,是#root.method.name的简写形式
#result 方法调用的返回值(不能用在@Cacheable注解上)
#Argument 任意的方法参数名或参数索引
@CachePut(value="user", key="#user.id")
User save(User user);
条件化缓存
@Cacheable和@CachePut提供了两个属性用于实现条件化缓存:unless和condition。这两个属性会接受一个SpEL表达式,如果unless属性的SpEL表达式计算结果为true,那么缓存方法返回的数据就不会放到缓存之中。如果condition属性的SpEL表达式计算结果为false,那么对于这个方法的缓存就会被禁用掉。unless只能阻止对象放进缓存,若调用该方法,依然会去缓存中进行查找,如果是找到了匹配的值,就返回找到的值。如果condition的表达式是false,在这个方法中,缓存是被禁用的,也就是说不会去缓存中查找,并且返回值也不会放进缓存中。
@Cacheable(value="user", unless="#resule.role.contains('Admin')", condition="#id >=10" ) User findOne(long id);
移除缓存条目
@CacheEvict表示当缓存值不再合法时,将其从缓存中移除。最常用在remove语句中
@CacheEvict("user")
void remove(long userId);
@Cacheable和@CachePut只能作用在非void返回值的方法上,@CacheEvict可以作用在任意的方法上。@CacheEvict有以下几个属性:
value String[] 要使用的缓存名称
key String SePL表达式,用来计算自定义的缓存key
condition String SpEL表达式,如果得到的值是false,缓存不会应用到方法调用上
allEntries boolean 若为true,特定缓存的所有条目都会被移除掉
beforeInvocation boolean 若为true,在方法调用之前移除条目,若为false,在方法调用之后再移除条目
使用XML声明缓存
一般有两个原因会使用XML声明缓存:可能觉得在自己的源码中添加Spring的注解不舒服和需要在没有源码的bean上应用缓存功能。
Spring的cache命名空间提供了使用XML声明缓存规则的方法,可以作为面向注解缓存的替代方案。因为缓存是一种面向切面的行为,所以cache命名空间会与Spring的aop命名空间结合起来使用,用来声明缓存所应用的切点在哪里
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" 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.xds http://www.springframework.org/schema/aop http://www.springframework.org/schema/spring-aop.xsd"> </beans>
cache命名空间定义了在Spring XML配置文件中声明缓存的配置元素
<cache:annotation-driven> 启动注解驱动的缓存,等同于Java配置中的@EnableCaching
<cache:advice> 定义缓存通知(advice)。结合<aop:advisor>,将通知应用到切点上
<cache:caching> 在缓存通知中,定义一组特定的缓存规则
<cache:cacheable> 指明某个方法要进行缓存,等同于@Cacheable注解
<cache:cache-put> 指明某个方法要填充缓存,但不会考虑缓存中是否已有匹配的值,等同于@CachePut注解
<cache-evict> 指明某个方法要从缓存中移除一个或多个条目,等同于@CacheEvict注解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:aop="http://www.springframework.org/schema/aop" 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.xds http://www.springframework.org/schema/aop http://www.springframework.org/schema/spring-aop.xsd"> <aop:config> <aop:advisor advice-ref="cacheAdvice" pointcut="execution(* com.cherry.repository.*(..))" /> </aop:config> <cache:advice id="cacheAdvice">
<cache:caching> <cache:cacheable cache="user" method="findOne"/> <cache:cache-put cache="user" method="save" key="#reuslt.id" /> <cache:cache-evict cache="user" method="remove" /> </cache:caching> </cache:advice>
<bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" /> </beans>
<cache:cache-evict>元素有两个特有的属性:
all-entries 如果是true,缓存中所有的条目都会被移除,如果是false,只有匹配key的条目才会被移除
before-invocation 如果是true,缓存条目将会在方法调用之前被移除,如果是false,方法调用之后才会被移除