springboot3默认的整合下,缓存时间未指定(默认永不失效),且序列化方式为Jdk的Serializable序列化方式,使用起来略有不便。Serializable序列化方式在序列化ID有变化,或者未指定序列化ID时,类的字段一变化,就会报错反序列化失败。
下面是一个可以指定指定缓存时间,指定默认缓存时间,并且使用JSON序列化方式的集成代码。

引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

设置默认的RedisTemplate

如果项目中是直接使用RedisTmplate来进行缓存的,需要指定RestTemplate的序列化方式,代码如下:

@UtilityClass
public class JacksonJsonSerializerUtil {
    public static GenericJackson2JsonRedisSerializer getJackson2JsonRedisSerializer() {
        var jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        jackson2JsonRedisSerializer.configure(objectMapper -> {
            // add java8 time module
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.activateDefaultTyping(new LaissezFaireSubTypeValidator(),
                    ObjectMapper.DefaultTyping.EVERYTHING);
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            objectMapper.registerModule(javaTimeModule);
        });
        return jackson2JsonRedisSerializer;
    }
}
    @Bean("redisTemplate")
    RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        var jackson2JsonRedisSerializer = JacksonJsonSerializerUtil.getJackson2JsonRedisSerializer();

        // 设置value的序列化规则和key的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

设置@Cacheable的序列化方式

@Cacheable不使用RedisTemplate,因此上面的定义是无效的,它直接使用CacheManager,因此我们需要自定义。spring提供了自定义的RedisCacheManagerBuilderCustomizer来支持自定义,我们实现它,并将它注入到spring容器中就可以了。

public class MyRedisCacheManagerBuilderSerialCustomizer implements RedisCacheManagerBuilderCustomizer {
    @Override
    public void customize(RedisCacheManager.RedisCacheManagerBuilder builder) {
        RedisSerializer<String> stringRedisSerializer = new StringRedisSerializer();

        var jackson2JsonRedisSerializer = getJackson2JsonRedisSerializer();
        var configuration = builder.cacheDefaults()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));

        builder.cacheDefaults(configuration);
        var configuredCaches = builder.getConfiguredCaches();

        // 针对有的缓存存在自定义配置的情况,读取所有缓存的配置,设置序列化方法
        for (String configuredCache : configuredCaches) {
            var cacheConfig = builder.getCacheConfigurationFor(configuredCache);
            cacheConfig.ifPresent(config -> {
                var newConfig = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
                builder.withCacheConfiguration(configuredCache, newConfig);
            });
        }
    }
}

// 注入到spring容器中
    @Bean
    MyRedisCacheManagerBuilderSerialCustomizer myRedisCacheManagerBuilderSerialCustomizer() {
        return new MyRedisCacheManagerBuilderSerialCustomizer();
    }

自定义缓存的过期时间,以及默认过期时间

我们自定义一个配置来支持设置过期时间,配置如下:

myservice:
  redis:
    # 采用ISO-8601时间格式。格式为:PnDTnHnMnS   (n为个数)
    #例如:P1Y2M3DT4H5M6.7S = 1年2个月3天4小时5分钟6.7秒
    default-expire-time: PT30M
    cache-and-expires:
      - cache-name: test
        expire-time: PT10M
@Data
public class CacheAndExpire {
    private String cacheName;
    private Duration expireTime;
}

@Slf4j
public class MyRedisCacheManagerBuilderCustomizer implements RedisCacheManagerBuilderCustomizer {
    private final MyRedisConfig myRedisConfig;

    public MyRedisCacheManagerBuilderCustomizer(MyRedisConfig myRedisConfig) {
        this.myRedisConfig = myRedisConfig;
    }

    @Override
    public void customize(RedisCacheManager.RedisCacheManagerBuilder builder) {
        log.info("my redis expire-time config!");
        var configuration = builder.cacheDefaults()
                .entryTtl(myRedisConfig.getDefaultExpireTime());

        builder.cacheDefaults(configuration);

        log.info("my redis expire-time cacheAndExpire has {} configs!", myRedisConfig.getCacheAndExpires().size());
        for (CacheAndExpire cacheAndExpire : myRedisConfig.getCacheAndExpires()) {
            var cacheConfig = builder.getCacheConfigurationFor(cacheAndExpire.getCacheName()).orElse(configuration);
            builder.withCacheConfiguration(cacheAndExpire.getCacheName(), cacheConfig.entryTtl(cacheAndExpire.getExpireTime()));
        }
    }
}

将bean注入到Spring容器中

@EnableConfigurationProperties({MyRedisConfig.class})
public class RedisConfig {

    @Bean
    MyRedisCacheManagerBuilderCustomizer myRedisCacheManagerBuilderCustomizer(MyRedisConfig myRedisConfig) {
        return new MyRedisCacheManagerBuilderCustomizer(myRedisConfig);
    }
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐