SpringBoot 缓存管理器CacheManager
从3.1开始Spring定义了org.springframework.cache.Cache
和org.springframework.cache.CacheManager
接口来统一不同的缓存技术;并支持使用JCache(JSR-107)
注解简化开发.
- Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
- Cache接口下Spring提供了各种xxxCache的实现;如
RedisCache
,EhCacheCache
,ConcurrentMapCache
等;
快速开始
1、导入Maven
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <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>
|
因为需要用RedisCacheManager
所以导入Redis依赖
2、配置Redis并开启缓存支持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| 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;
@Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Resource private LettuceConnectionFactory lettuceConnectionFactory;
@Bean public CacheManager cacheManager() { RedisSerializer<String> redisSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
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.ofSeconds(20)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)) .disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(lettuceConnectionFactory) .cacheDefaults(config) .build(); return cacheManager; }
@Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { 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<String, Object> redisTemplate = new RedisTemplate<String, Object>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); RedisSerializer<?> stringSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringSerializer); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(stringSerializer); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); redisTemplate.afterPropertiesSet(); return redisTemplate; }
}
|
3、注解解析
注解的几个属性说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @AliasFor("cacheNames") String[] value() default {};
@AliasFor("value") String[] cacheNames() default {};
String key() default "";
String keyGenerator() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
String unless() default "";
boolean sync() default false;
boolean allEntries() default false;
boolean beforeInvocation() default false;
|
@Cacheable
先查询缓存中是否存在,存在则返回缓存内容,反正执行方法后返回并把返回结果缓存起来
@CacheEvict
删除指定缓存
@CachePut
更新并刷新缓存,先执行方法内容,然后更新缓存
@EnableCaching
这个是一个复合注解,可以拥有同时配置上面3个注解的功能
4、使用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| @CacheConfig(cacheNames = "act") @Service public class ActicleService implements IActicleService {
@Autowired private ActicleMapper acticleMapper;
@Cacheable(key = "#root.methodName") public List<Acticle> list() throws Exception { return acticleMapper.getActicleList(); }
@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; }
@CacheEvict(key = "#id",beforeInvocation = true) public int del(Long id) throws Exception { int isDel = 0; isDel = acticleMapper.del(id); return isDel; }
@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(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; } }
|
缓存工作原理
当我们引入缓存的时候,SpringBoot的缓存自动配置CacheAutoConfiguration
就会生效
- 在
CacheAutoConfiguration
中的CacheConfigurationImportSelector
会导入很多缓存组件配置类
- 通过debug看源码,知道imports 的内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 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; } }
|
- 如果没有引
Redis
, 则SimpleCacheConfiguration
这个就是默认的配置类
- 在
SimpleCacheConfiguration
注入了一个ConcurrentMapCacheManager
ConcurrentMapCacheManager
实现了CacheManager
接口
ConcurrentMapCacheManager
通过 ConcurrentHashMap
把数据缓存起来
- 在
CacheManager
有一个Cache getCache(String var1)
方法换取缓存
- 流程大概就这里,感兴趣的同学可以打断点阅读源码
本章代码示例地址