Spring缓存 spring都有哪几种方式能实现缓存?为什么以及什么情况下需要用到、更新、删除缓存
启用支持
1:注解驱动的缓存 @Cacheable @CacheEvict
2:xml声明的缓存
如果使用java配置,需要在其中一个配置类上添加@EnableCaching
@EnableCaching
@Configuration
public class CacheConfig {
@Bean
// 声明缓存管理器
public CacheManager cacheManager(){
return new ConcurrentMapCacheManager();
}
}
如果使用xml配置
本质上,都是创建一个切面并触发spring缓存注解的切点。根据所使用的注解以及缓存状态,这个切面会从缓存获取数据,将数据添加到缓存或从缓存中移除
ConcurrentMapCacheManager这个简单的缓存管理器使用java.util.concurrent.ConcurrentHashMap作为缓存存储(测试,基于内存)
- 配置缓存管理器
SimpleCacheManager
NoOpCacheManager
ConcurrentMapCacheManager
CompositeCacheManager
EhCacheCacheManager
RedisCacheManager(来自于Spring Data Redis项目)
GemfireCacheManager(来自于Spring Data GemFire项目)
1:使用ehcache
EhcacheConfig.class
/**
* 声明一个CacheManager bean
* Spring提供了EhCacheManager-FactoryBean来生成EhCache的CacheManager
*
*/
@Bean
public EhCacheManagerFactoryBean ehcache(){
EhCacheManagerFactoryBean eb = new EhCacheManagerFactoryBean();
eb.setConfigLocation(new ClassPathResource("spittr/cache/ehcache.xml"));
return eb;
}
/**
* 注:
* org.springframework.cache.ehcache
* 需要导入spring-context-support,且还需要额外导入net.sf.ehcache
* 通过传入EhcacheCacheManager实例实现
* EhCache的CacheManager要被注入到Spring的EhCacheCacheManager
*
*/
@Bean
public EhCacheCacheManager cacheManager(CacheManager cm){
return new EhCacheCacheManager(cm);
}
ehcache.xml:
<ehcache>
<cache name="spittleCache" maxBytesLocalHeap="50m" timeToLiveSeconds="100"/>
</ehcache>
2:使用redis缓存
如果缓存的条目是键值对形式的,那非常适合用redis存储
需要引入org.springframework.data.reids & redis.client
redis提供了RedisCacheManager,是CacheManager的实现,会与redis服务器协作,通过RedisTemplate将缓存条目存储到Redis中
@Bean
public JedisPoolConfig jedisPoolConfig(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxWaitMillis(1000);
return jedisPoolConfig;
}
@Bean
public JedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPoolConfig){
JedisConnectionFactory jf = new JedisConnectionFactory();
jf.setPoolConfig(jedisPoolConfig);
jf.setHostName("localhost");
jf.setPort(6379);
jf.afterPropertiesSet();
return jf;
}
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate){
return new RedisCacheManager(redisTemplate);
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
注:报错// DefaultSerializer requires a Serializable payload but received an object of type [spitter.Spittle]
对应类必须实现Serializable
为方法添加注解支持缓存
@Cacheable 表明Spring在调用方法之前,首先应该在缓存中查找方法的返回值。如果这个值能够找到,就会返回缓存的值。否则的话,这个方法就会被调用,返回值会放到缓存之中
@CachePut 表明Spring应该将方法的返回值放到缓存中。在方法的调用前并不会检查缓存,方法始终都会被调用
@CacheEvict 表明Spring应该在缓存中清除一个或多个条目
@Caching 这是一个分组的注解,能够同时应用多个其他的缓存注解
- 填充缓存
/**
* 缓存切面会拦截调用并在缓存中查找之前以名spittleCache存储的返回值
* 缓存的key是传递到findOne()方法中的id参数
* 如果按照这个key能够找到值的话,就会返回找到的值,方法不会再被调用
* 但是这样仅限于这个实现类,为了避免出现这个问题,可以将Cacheable声明在接口方法上
* 带有@CachePut的方法始终会被调用,可以实现预加载
* 例如,可以在save(Spittle spittle)方法上添加@CachePut("spittleCache")
* 但是这样回导致key为Spittle,所以需要指定key
* @CachePut("spittleCache", key="#result.id") <= 表达式#result能够得到返回的Spittle
* 此外,还有当数据有修改时,可以通过CachePut强制更新
*/
@Cacheable("spittleCache")
public Spittle findSpittels(int id) {
System.out.println("调用一次:" + id);
return jdbc.queryForObject("select message, created_at, longitude,latitude from Spittle where id = ?",
new SpitterRowMapper(),
id);
}
注:这里遇到一个问题,1.7.0的spring-data-redis返回RedisCacheManager对象,其父类没有实现CacheManager接口,是因为没有spring-support的jar或者对应jar的版本与spring版本不匹配
- 条件化缓存
某些情况下,可能只有符合某些条件下我们才会使用缓存
/**
* unless:如果为true,阻止将对象放入缓存,但是如果缓存中存在,依然从缓存中读取
* condition:如果condition返回false,禁用缓存
*/
@CachePut(value = "spitterCache", key = "#result.username", unless = "#result.username.contains('NoCache')")
