《Spring实战》学习笔记-------使用缓存

启用对缓存的支持

在Java的Config配置类上:

@Configuration
@EnableCaching   //启用缓存
public class CachingConfig{
	@Bean
	public CacheManager cacheManager(){  //声明缓存管理器
		return new ConcurrentMapCacheManager(); 
	}
}

@EnableCaching和XML中的<cache:annotationn-driven />的工作方式相同,它们会创建一个切面并触发Sping缓存注解的切点,然后进行缓存操作。

配置时还声明了一个CacheManager缓存管理器。这个缓存管理器使用java.util.concurrent.ConcurrentHashMap作为其缓存存储。但它的缓存存储是基于内存的。

我们还可以根据自己的需要配置其它的缓存管理器。

配置缓存管理器

Spring 3.1内置了五个缓存管理器实现:

  • SimpeCacheManager
  • NoOpCacheManager
  • ConcurrentMapCacheManager
  • CompositeCacheManager
  • EhCacheCacheManager

Spring Data又提供了两个缓存管理器:

  • RedisCacheManager(来自于Spring Data Redis)
  • GemfireCacheManager(来自于Spring Data GemFire)

使用EhCache缓存

在Java的Config配置类中:

@Configuration
@EnableCaching   //启用缓存
public class CachingConfig{
	@Bean                             //这里是net.sf.ehcache.CacheManager
	public EhCacheManager cacheManager(CacheManager cm){  //配置EhCacheCacheManager
		return new EhCacheCacheManager(cm);  //需要注入一个CacheManager,下面声明了这个bean
		//返回的是 org.springframework.cache.ehcache.EhCacheCacheManager
	}

	@Bean  
	public EhCacheManagerFactoryBean ehcache(){  //配置EhCacheManagerFactoryBean
		EhCacheManagerFactoryBean ecmfBean = new EhCacheManagerFactoryBean();
		ecmfBean.setConfiguration(new ClassPathResource("xxxx/ehcache.xml"));
		return ecmfBean;
		//实际上它是CacheManager的实例,而不是EhCacheManagerFactoryBean的实例
	}
}

第一个方法,cacheManager会创建一个EhCacheCacheManager的实例。因为Spring和EhCache都定义了CacheManager类型,是EhCache的CacheManager被注入到了Spring的EhCacheCacheManager(Spring中CacheManager的实现)中。

第二个方法,因为第一步需要一个EhCache的CacheManager进行注入,所以必须要声明一个CacheManager的bean,也就是第二个方法所得到的这个bean。Spring提供了EhCacheManagerFactoryBean(它实现了FactoryBean接口)来生成EhCache的CacheManager。

除了配置类,我们还需要一个EhCache的XML配置文件ehcache.xml:

<ehcache>
	<cache name="spittleCache"
			maxBytesLocalHeap="50m"  
			timeToLiveSeconds="100" /> <!-- 最大堆存储为50m,生命周期100s -->
</ehcache>

使用Redis缓存

Spring Data Redis提供的RedisCacheManager也是CacheManager的一个实现。

RedisCacheManager会与一个Redis服务器协作,通过RedisTemplate将缓存储存到Redis中。

所以我们需要一个RedisTemplate的bean,和一个RedisConnectionFactory(连接工厂)实现类的bean。

Java的Config配置类:

@Configuration
@EnableCaching
public class CachingConfig{

	@Bean                        //Redis缓存管理器Bean
	public CacheManager cacheManager(RedisTemplate redisTemplate){
		return new RedisCacheManager(redisTemplate);
	}

	@Bean                        //Redis连接工厂bean,也可以选择其它类型的连接工厂
	public JedisConnectionFactory redisConnectionFactory(){
		JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
		jedisConnectionFactory.afterPropertiesSet(); //
		return jedisConnectionFactory;
	}

	@Bean                       //RedisTemplate bean
	public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisCF){
		RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
		redisTemplate.setConnectionFactory(redisCF);
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}
}

使用多个缓存管理器

也可以使用CompositeCacheManager迭代已经添加的缓存管理器,来查找缓存值。

例如,将Redis示例中CachingConfig类和EhCache示例中CachingConfig类中的Bean合到一个Config类中,并将cacheManager修改成:

@Bean
public CacheManager cacheManager(net.cf.ehcache.CacheManager cm, //EhCache
								javax.cache.CacheManager jcm){ //JCache
	CompositeCacheManager cacheManager = new CompositeCacheManager();
	List<CacheManager> managers = new ArrayList<>();
	managers.add(new JCacheCacheManager(jcm));
	managers.add(new EhCacheCacheManager(cm));
	managers.add(new RedisCacheManager(redisTemplate())); //注入redisTemplate bean,该方法在类中已声明
	cacheManager.setCacheManagers(managers);
	return cacheManager;
}

查找缓存时,会按照JCache、EhCache、RedisCache的顺序来查找。


为方法添加注解以支持缓存

Spring的缓存是围绕着切面来构建的,启用缓存时会创建一个切面,它触发Spring的缓存注解。

Spring提供了四个注解来声明缓存规则,它们可以用在方法或类上,如果放在类级别,缓存行为会应用这个类所以的方法。

注解描述
@Cacheable在调用方法之前,首先应该在缓存中查找方法的返回值。如果找到就返回该值,否则再调用该方法,返回值会放到缓存中
@CachePut将方法的返回值放到缓存中。并且在方法的调用前不会检查缓存,方法始终都被调用
@CacheEvict在缓存中清除一个或多个条目
@Caching这是一个分组的注解,能够同时应用多个其它的缓存注解

@Cacheable和@CachePut注解都可以填充缓存,所以有4个共有属性:

属性类型描述
valueString[]要使用的缓存名称
conditionStringSpEL表达式,如果得到的值是false,不会将缓存应用到方法调用上
keyStringSpELl表达式,用来计算自定义的缓存key
unlessStringSpEL表达式,如果得到true,返回值不会放入缓存中

在查询操作时,只需使用value属性就可以完成缓存。例如:SpittleRepository的findOne()方法:

@Cacheable("spittleCache") //使用spittleCache缓存
//通常情况下,@Cacheable注解应该放在接口方法声明类上,而不是实现类上
public Spittle findOne(long id){ 
	try{
		return "进行查询操作,得到一个Spittle对象返回";
	} catch (Exception e){
		return null;
	}
}

findOne()方法被调用时,缓存切面会拦截调用,并在缓存中查找以名SpittleCache存储的返回值。缓存的key是传入的id参数。

将值放入缓存

@CachePut注解通常使用在插入记录时。例如,一个新的Spittle对象调取save()方法保存后,很可能马上会请求查看这个记录。

@CachePut("SpittleCache")
Spittle save(Spittle spittle);//必须有返回值缓存才能生效

当save方法被调用时,会先执行方法,然后将返回值放入缓存。但是不能将参数spittle作为key,我们需要另外指明它的key为spittleId。

通过@CachePut或@Cacheable注解的key属性,可以计算声明缓存的key。

Spring提供的SpEL表达式有:

表达式描述
#root.args传递给方法的参数,形式为数组
#root.caches该方法执行时对应的缓存,形式为数组
#root.target目标对象
#root.targetClass目标对象的类
#root.method缓存方法
#root.methodName缓存方法的名字
#result方法调用的返回值(不能用在注解的condition属性上)
#Argument任意的方法参数名(如#argName)或参数索引(#a0或#p0)

对于save方法,#result可以获得返回的对象,可以补充为:

@CachePut(value="spittleCache", key="#result.id")
Spittle save(Spittle spittle);

条件化缓存

@Cacheable和@CachePut注解提供了两个属性来实现条件化缓存:unless和condition。

  • unless:SpEL表达式属性为true,数据不进行缓存。
  • condition: SpELl表达式属性为false,数据不进行缓存。

unless属性在方法调用的时候会先在缓存中查找,而condition生效时,缓存禁用,不进行查找也不存储。

例如:对于message包含"NoCache"字段的Spittle对象不存入缓存:

@Cacheable(value="spittleCache", unless="#result.message.contains('NoCache')");
Spittle findOne(long id);

对于id小于10的Spittle对象禁用缓存,不从缓存中查找也不存入缓存:

@Cacheable(value="spittleCache", 
			unless="#result.message.contains('NoCache')", 
			condition="#id >= 10")
Spittle findOne(long id);

condition目的是决定要不要禁用缓存,所以它不能等到已经返回值了才确定是否禁用缓存。

移除缓存条目

@CacheEvict注解会从缓存中移除条目,常见应用场景是删除了某一条数据:

@CacheEvict("spittleCache")
void remove(long spittleId);//CacheEvict注解可以应用在返回值为void方法上

remove()方法被调用时,会删除key为spittleId的缓存记录。

@CacheEvict注解的可用属性如下:

属性类型描述
valueString[]要使用的缓存名
keyStringSpELl表达式,用来计算缓存的key
conditionStringSpELl表达式,得到false就不会应用缓存
allEntriesboolean如果为true,缓存的所以条目都被移除
beforeInvocationboolean如果为true,在方法调用之前移除条目;如果为false(默认),在方法成功调用之后移除条目

使用XML声明缓存

XML文件中要包含cache和aop的命名空间。

cache命名空间提供的标签:

标签描述
<cache:annotation-driven>启用注解驱动的缓存。等同于@EnableCaching
<cache:advice>定义缓存通知。结合<aop:advisor>,将通知应用到切点上
<cache:caching>在缓存通知中,定义一组特定的缓存规则
<cache:cacheable>指明某个方法要进行缓存。等同于@Cacheable注解
<cache:cache-put>指明某个方法要填充缓存。等同于@CachePut
<cache:cache-evict>指明某个方法要从缓存中移除一个或多个条目,等同于@CacheEvict

示例:

<aop:config>
	<aop:advisor advice-ref="cacheAdvice" 
		pointcut="execution(* xxxx.SpittleRepository.*(..))" /> <!-- 将缓存通知绑定到切点上-->
</aop:config>

<cache:advice id="cacheAdvice" cache-manager="cacheManager"> <!-- 指定缓存管理器-->
	<cache:caching>
		<cache:cacheable cache="spittleCache" method="findRecent" /> <!-- 配置方法支持缓存-->
		<cache:cacheable cache="spittleCache" method="findOne" /> 
		<cache:cacheable cache="spittleCache" method="findBySpittleId" />
		<cache:cache-put cache="spittleCache" method="save" key="#result.id" /> <!-- 配置方法填充缓存-->
		<cache:cache-evict cache="spittleCache" method="remove" /> <!-- 配置方法移除缓存-->
	</cache:caching>
</cache:advice>

<bean id="cacheManager" class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" />

<aop:advisor>引用ID为cacheAdvice的通知,它将这个通知与一个切点匹配,建立了一个完整的切面。它需要指定一个缓存管理器,默认值是cacheManager,所以下面有一个cacheManager的bean。
通知利用<cache:advice>元素声明,在<cache:advice>下面可以包含任意数量的<cache:caching>。

<cache:caching>有四个可以供子标签共享的属性:

  • cache:指明要使用的缓存名称
  • condition:SpEL表达式,false禁用缓存。
  • key:SpEL表达式,用来得到缓存的key
  • method:要缓存的方法名
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值