SpringBoot 緩存管理器CacheManager
從3.1開始Spring定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口來統(tǒng)一不同的緩存技術(shù);并支持使用JCache(JSR-107) 注解簡化開發(fā).
- Cache接口為緩存的組件規(guī)范定義,包含緩存的各種操作集合;
- Cache接口下Spring提供了各種xxxCache的實(shí)現(xiàn);如
RedisCache,EhCacheCache,ConcurrentMapCache等;
快速開始
1、導(dǎo)入Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
因?yàn)樾枰?code>RedisCacheManager 所以導(dǎo)入Redis依賴
2、配置Redis并開啟緩存支持
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
import java.time.Duration;
/**
*
* @author rstyro
* @time 2018-07-31
*
*/
@Configuration
@EnableCaching // 開啟緩存支持
public class RedisConfig extends CachingConfigurerSupport {
@Resource
private LettuceConnectionFactory lettuceConnectionFactory;
/**
* 配置CacheManager
* @return
*/
@Bean
public CacheManager cacheManager() {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解決查詢緩存轉(zhuǎn)換異常的問題
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解決亂碼的問題)
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
// .entryTtl(Duration.ZERO)
.entryTtl(Duration.ofSeconds(20)) //設(shè)置緩存失效時(shí)間
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(lettuceConnectionFactory)
.cacheDefaults(config)
.build();
return cacheManager;
}
/**
* RedisTemplate配置
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
// 設(shè)置序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
RedisSerializer<?> stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);// key序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
3、注解解析
注解的幾個屬性說明:
//指定緩存組件的名字
@AliasFor("cacheNames")
String[] value() default {};
//指定緩存組件的名字
@AliasFor("value")
String[] cacheNames() default {};
// 緩存數(shù)據(jù)使用的Key,
String key() default "";
// key的生成器,可以自己指定key的生成組件id;
// key 與 keyGenerator 兩個只能選一個,不能同時(shí)指定
String keyGenerator() default "";
// 指定緩存管理器,
String cacheManager() default "";
//指定獲得解析器,cacheManager、cacheResolver 兩者取其一
String cacheResolver() default "";
// 符合condition條件,才緩存
String condition() default "";
// 和 codition 條件相反才成立
String unless() default "";
// 是否使用異步模式
boolean sync() default false;
// 只有@CacheEvict 有這個屬性
// 清空所有緩存信息
boolean allEntries() default false;
// 只有@CacheEvict 有這個屬性
// 緩存的清除是否在方法之前執(zhí)行
// beforeInvocation=false 默認(rèn)是在方法之后執(zhí)行
boolean beforeInvocation() default false;
-
@Cacheable
先查詢緩存中是否存在,存在則返回緩存內(nèi)容,反正執(zhí)行方法后返回并把返回結(jié)果緩存起來 -
@CacheEvict
刪除指定緩存 -
@CachePut
更新并刷新緩存,先執(zhí)行方法內(nèi)容,然后更新緩存 -
@EnableCaching
這個是一個復(fù)合注解,可以擁有同時(shí)配置上面3個注解的功能
4、使用方法
@CacheConfig(cacheNames = "act") //這個注解表示類中共同放入到act 模塊中
@Service
public class ActicleService implements IActicleService {
@Autowired
private ActicleMapper acticleMapper;
/**
* @Cacheable
* 1、先查緩存,
* 2、若沒有緩存,就執(zhí)行方法
* 3、若有緩存。則返回,不執(zhí)行方法
*
* 所以@Cacheable 不能使用result
*
* @return
* @throws Exception
*/
@Cacheable(key = "#root.methodName")
public List<Acticle> list() throws Exception {
return acticleMapper.getActicleList();
}
/**
* @CachePut 更新并刷新緩存
* 1、先調(diào)用目標(biāo)方法
* 2、把結(jié)果緩存
* @param acticle
* @return
* @throws Exception
*/
@CachePut(key = "#result.id" ,unless = "#result.id == null" )
public Acticle save(Acticle acticle) throws Exception {
acticle.setCreateBy(1l);
acticle.setCreateTime(LocalDateTime.now());
acticle.setModifyBy(1l);
acticle.setModifyTime(LocalDateTime.now());
acticleMapper.save(acticle);
System.out.println("acticle="+acticle);
return acticle;
}
/**
* 刪除指定key 的緩存
* beforeInvocation=false 緩存的清除是否在方法之前執(zhí)行
* 默認(rèn)是在方法之后執(zhí)行
* @param id
* @return
* @throws Exception
*/
@CacheEvict(key = "#id",beforeInvocation = true)
public int del(Long id) throws Exception {
int isDel = 0;
isDel = acticleMapper.del(id);
return isDel;
}
/**
* 刪除所有緩存
* @return
* @throws Exception
*/
@CacheEvict(allEntries = true)
public int delAll() throws Exception {
return 1;
}
@CachePut(key = "#result.id" ,unless = "#result.id == null" )
public Acticle update(Acticle acticle) throws Exception {
acticle.setModifyBy(1l);
acticle.setModifyTime(LocalDateTime.now());
return acticleMapper.update(acticle);
}
@Cacheable(key = "#id",condition = "#id > 0")
public Acticle queryById(Long id) throws Exception {
return acticleMapper.queryById(id);
}
/**
* @Caching復(fù)雜組合緩存注解
*
* @param title
* @return
* @throws Exception
*/
@Caching(cacheable = { @Cacheable(key = "#title")},
put = {@CachePut(key = "#result.id"),
// @CachePut(key = "T(String).valueOf(#page).concat('-').concat(#pageSize)")
@CachePut(key = "T(String).valueOf('tag').concat('-').concat(#result.tagId)")
})
public Acticle queryByTitle(String title) throws Exception {
return acticleMapper.queryByTitle(title);
}
@Cacheable(key = "T(String).valueOf('tag').concat('-').concat(#tagId)")
public Acticle queryByTag(Long tagId) throws Exception {
return null;
}
}
緩存工作原理
當(dāng)我們引入緩存的時(shí)候,SpringBoot的緩存自動配置CacheAutoConfiguration就會生效
- 在
CacheAutoConfiguration中的CacheConfigurationImportSelector會導(dǎo)入很多緩存組件配置類 - 通過debug看源碼,知道imports 的內(nèi)容如下
static class CacheConfigurationImportSelector implements ImportSelector {
CacheConfigurationImportSelector() {
}
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length];
for(int i = 0; i < types.length; ++i) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
}
return imports;
}
}
/**
imports 如下
0 = "org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration"
1 = "org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration"
2 = "org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration"
3 = "org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration"
4 = "org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration"
5 = "org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration"
6 = "org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration"
7 = "org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration"
8 = "org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration"
9 = "org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration"
*/
- 如果沒有引
Redis, 則SimpleCacheConfiguration這個就是默認(rèn)的配置類 - 在
SimpleCacheConfiguration注入了一個ConcurrentMapCacheManager -
ConcurrentMapCacheManager實(shí)現(xiàn)了CacheManager接口 -
ConcurrentMapCacheManager通過ConcurrentHashMap把數(shù)據(jù)緩存起來 - 在
CacheManager有一個Cache getCache(String var1)方法換取緩存 - 流程大概就這里,感興趣的同學(xué)可以打斷點(diǎn)閱讀源碼