SpringBoot & CacheManager

  • 使用注解 @EnableCaching 启用缓存
    • 一般是添加在自定义 CacheManager 的配置类上
  • 使用 @Cacheable@CachePut@CacheEvict 来维护缓存
    • @Cacheable:先查询缓存,不存在时才执行方法体
    • @CachePut:始终执行方法体,并更新缓存
    • @CacheEvict:始终执行方法体,并删除缓存
    • 接口方法为实体类型,而缓存 Key 只使用了其中的字段时,通过 key 参数来指定,格式为 #参数变量名.字段名
  • 使用 RedisCacheManagerBuilderCustomizer 来配置缓存
    • 这里使用的 Jackson

MyCacheConfiguration.java

自定义缓存 KeyValue 的序列化方法。
这里使用带类型的 Json 序列化方法。如果不带类型,在反序列化时,会自动反序列化为 HashMap 类型,从而导致在转换为方法的返回值时转型失败。

package com.mokasz.zhyx.hyperion.free.novel.core.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mokasz.zhyx.hyperion.free.novel.core.constant.CacheName;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
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.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration(proxyBeanMethods = false)
@EnableCaching
public class MyCacheConfiguration {

    @Bean
    public RedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer() {
        StringRedisSerializer keySerializer = new StringRedisSerializer();

        Jackson2JsonRedisSerializer<Object> valueSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        valueSerializer.setObjectMapper(mapper);

        return (builder) -> builder
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofMinutes(30))
                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer)))
                .withCacheConfiguration(CacheName.NOVEL_DETAIL, RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ZERO)
                        .disableCachingNullValues()
                        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer)));

    }

}
  

NovelServiceImpl.java

缓存 Key 默认为指定的 value + :: + 所有参数。

package com.mokasz.zhyx.hyperion.free.novel.bll.service.impl;

import com.mokasz.zhyx.hyperion.free.novel.bll.service.NovelService;
import com.mokasz.zhyx.hyperion.free.novel.core.constant.CacheName;
import com.mokasz.zhyx.hyperion.free.novel.dao.entity.Novel;
import com.mokasz.zhyx.hyperion.free.novel.dao.service.NovelDaoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
@Slf4j
public class NovelServiceImpl implements NovelService {

    private final NovelDaoService novelDaoService;

    @Cacheable(value = CacheName.NOVEL_DETAIL, unless = "#result == null")
    @Override
    public Novel getNovel(int id) {
        return this.novelDaoService.getById(id);
    }

    @CachePut(value = CacheName.NOVEL_DETAIL, key = "#novel.id", unless = "#result == null")
    @Override
    public Novel createNovel(Novel novel) {
        this.novelDaoService.createNovel(novel);
        return this.novelDaoService.getById(novel.getId());
    }

    @CachePut(value = CacheName.NOVEL_DETAIL, key = "#novel.id", unless = "#result == null")
    @Override
    public Novel updateNovel(Novel novel) {
        this.novelDaoService.updateNovel(novel);
        return this.novelDaoService.getById(novel.getId());
    }

    @CacheEvict(CacheName.NOVEL_DETAIL)
    @Override
    public boolean deleteNovel(Integer id) {
        return this.novelDaoService.removeById(id);
    }
}