一、 JSR107规范
Java Caching定义了5个核心接口,分别是CachingProvider、CacheManager、Cache、Entry、Expiry。

相关接口的含义请参考:
二、CacheManager & Cache接口
本文选择RedisCacheManager和RedisCache作为示例:


二、核心注解
Spring中缓存的核心注解包括:@Cacheable、@CacheEvict、@CachePut、@EnableCaching、@CacheConfig、 @Caching等。注解使用方法请参考官方文档:
三、关键步骤
示例源码见:https://github.com/just-right/springbootcache
3.1 前期准备
本文使用Docker安装MySQL和Redis,Docker相关命令请参考:
启动MySQL和Redis容器:
启动Redis容器
docker run -d --name MyRedis001 -p 6379:6379 redis:latest --requirepass "123456" --protected-mode no --bind 0.0.0.0
启动MySQL容器
docker run -itd --name mysql001 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:latest
3.2 序列化
本文使用Jackson2JsonRedisSerializer作为缓存的序列化实现方式。核心代码如下所示:
private Jackson2JsonRedisSerializer<Object> buildJackson2JsonRedisSerializer(){
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//解决java.lang.ClassCastException:
//java.util.LinkedHashMap cannot be cast to XXX
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}
3.3 注入RedisTemplate
注入RedisTemplate,核心代码如下所示:
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 设置序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = this.buildJackson2JsonRedisSerializer();
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
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.4 注入CacheManager
注入CacheManager,设置缓存默认过期时间,支持根据cacheNames配置指定的过期时间,核心代码如下所示:
@Bean
@Primary
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(factory),
this.getRedisCacheConfigurationWithTtl(30), // 默认策略,未配置的 key 会使用这个
this.getRedisCacheConfigurationMap() // 指定 key 策略
);
}
默认策略,设置过期时间30秒:
private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = this.buildJackson2JsonRedisSerializer();
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
RedisSerializationContext
.SerializationPair
.fromSerializer(jackson2JsonRedisSerializer)
).entryTtl(Duration.ofSeconds(seconds));
return redisCacheConfiguration;
}
定制策略,根据cacheNames(student)设置过期时间2秒:
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
redisCacheConfigurationMap.put(CacheConst.CACHE_STUDENT_NAME, this.getRedisCacheConfigurationWithTtl(2));
return redisCacheConfigurationMap;
}
3.5 注入KeyGenerator
注入KeyGenerator,定制缓存生成的key值,核心代码如下所示:
@Bean
public KeyGenerator cacheKeyGenerator() {
return new StudentCacheKeyGenerator();
}
实现KeyGenerator接口,取cacheNames和id作为key值,如:student:1。
public class StudentCacheKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder key = new StringBuilder(CacheConst.CACHE_STUDENT_NAME + ":");
if (objects.length > 0) {
for (Object object : objects) {
if(object instanceof Student){
key.append(((Student) object).getId());
}else {
key.append(object.toString());
}
break;
}
}
return key.toString();
}
}
四、测试
测试Controller代码如下所示:
@RestController
@RequestMapping("student")
public class StudentController {
@Resource
private StudentService studentService;
@GetMapping("selectOne")
public Student selectOne(Integer id) {
return this.studentService.queryById(id);
}
@PutMapping("updateOne")
public Student updateOne(@RequestBody Student student) {
return this.studentService.update(student);
}
@DeleteMapping("deleteOne")
public boolean deleteOne(Integer id) {
return this.studentService.deleteById(id);
}
@PostMapping("addOne")
public Student addOne(@RequestBody Student student) {
return this.studentService.insert(student);
}
}
添加打印SQL信息配置,添加配置后可以通过查看控制台是否打印SQL信息判断是否使用缓存或缓存是否过期。
logging.level.com.example.cache.dao=debug
使用PostMan进行测试,相关测试信息如下图所示。




网友评论