一冠摄、 JSR107規(guī)范
??Java Caching定義了5個(gè)核心接口糯崎,分別是CachingProvider、CacheManager河泳、Cache沃呢、Entry、Expiry拆挥。
??相關(guān)接口的含義請(qǐng)參考:
二薄霜、CacheManager & Cache接口
??本文選擇RedisCacheManager和RedisCache作為示例:
二、核心注解
??Spring中緩存的核心注解包括:@Cacheable竿刁、@CacheEvict黄锤、@CachePut、@EnableCaching食拜、@CacheConfig鸵熟、 @Caching等。注解使用方法請(qǐng)參考官方文檔:
三负甸、關(guān)鍵步驟
??示例源碼見:https://github.com/just-right/springbootcache
3.1 前期準(zhǔn)備
??本文使用Docker安裝MySQL和Redis流强,Docker相關(guān)命令請(qǐng)參考:
??啟動(dòng)MySQL和Redis容器:
啟動(dòng)Redis容器
docker run -d --name MyRedis001 -p 6379:6379 redis:latest --requirepass "123456" --protected-mode no --bind 0.0.0.0
啟動(dòng)MySQL容器
docker run -itd --name mysql001 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:latest
3.2 序列化
??本文使用Jackson2JsonRedisSerializer作為緩存的序列化實(shí)現(xiàn)方式。核心代碼如下所示:
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) {
// 設(shè)置序列化
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打月,設(shè)置緩存默認(rèn)過期時(shí)間,支持根據(jù)cacheNames配置指定的過期時(shí)間蚕捉,核心代碼如下所示:
參考鏈接:SpringBoot 2.X @Cacheable奏篙,redis-cache 如何根據(jù)key設(shè)置緩存時(shí)間?
@Bean
@Primary
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
return new RedisCacheManager(
RedisCacheWriter.nonLockingRedisCacheWriter(factory),
this.getRedisCacheConfigurationWithTtl(30), // 默認(rèn)策略迫淹,未配置的 key 會(huì)使用這個(gè)
this.getRedisCacheConfigurationMap() // 指定 key 策略
);
}
?? 默認(rèn)策略秘通,設(shè)置過期時(shí)間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;
}
?? 定制策略,根據(jù)cacheNames(student)設(shè)置過期時(shí)間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();
}
??實(shí)現(xiàn)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();
}
}
四、測(cè)試
??測(cè)試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信息配置诲锹,添加配置后可以通過查看控制臺(tái)是否打印SQL信息判斷是否使用緩存或緩存是否過期繁仁。
logging.level.com.example.cache.dao=debug
??使用PostMan進(jìn)行測(cè)試,相關(guān)測(cè)試信息如下圖所示辕狰。