SpringBoot中配置过期键redis

1,226 阅读2分钟

起因

项目中希望在springboot中配置使用缓存,在这里将网上的综合配置记录一下。

基于spring中注解方式@Cacheable

1.pom.xml

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

2.添加@EnableCaching

@Slf4j
@SpringBootApplication
@MapperScan("com.my.equipment.web")
@EnableCaching
@EnableTransactionManagement 
public class Application {


    public static void main(String[] args) {
        ApplicationContext context= SpringApplication.run(Application.class,args);
        String serverPort=context.getEnvironment().getProperty("server.port");
        log.info("start at http://localhost:"+serverPort);

    }
}

补充

关于@Cacheable 的具体参数可以参考SpringBoot 缓存之 @Cacheable 详细介绍 - InfoQ 写作平台

  • @CachePut

    更新 Redis 中对应键的值。属性和 @Cacheable 相同

注意:

  1. cacheNames 和 key 要跟 @Cacheable() 里的一致,才会正确更新。
  2. @CachePut() 和 @Cacheable() 注解的方法返回值要一致
  • @CacheEvict

    删除 Redis 中对应键的值。 在需要删除缓存的方法上加注解:@CacheEvict(cacheNames = "prodcut", key = "123"),执行完这个方法之后会将 Redis 中对应的记录删除。

此外:cacheNames 也可以统一写在类上面, @CacheConfig(cacheNames = "product") ,具体的方法上就不用写。

@Service
@CacheConfig(cacheNames = "simSlave")
public class SimSlaveServiceImpl implements SimSlaveService {


    @Transactional
    @Cacheable(key = "#id")
    public SlaveSimData getSlaveData(int id) {
        SlaveSimData slaveSimData = slaveSimDataMapper.selectByPrimaryKey(id);
        System.out.println("生成数据缓存"+id);
        return slaveSimData;
    }
}

存在问题:无法设置有过期时间的

解决方法:自定义redisManager

package com.my.equipment.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class CacheConfig {

    @Autowired
    ResourceLoader resourceLoader;

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        //设置 json 序列化
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).
                // 设置过期时间 entryTtl()参数当小于等于0时,将缓存时间设置为永久
                entryTtl(Duration.ofSeconds(seconds));

        return redisCacheConfiguration;
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        redisCacheConfigurationMap.put("simSlave", this.getRedisCacheConfigurationWithTtl(60 * 60 ));
        return redisCacheConfigurationMap;
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
      //使用默认的redisManager管理器
       return new RedisCacheManager(
               RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
               this.getRedisCacheConfigurationWithTtl(-1),
               this.getRedisCacheConfigurationMap()
       );
  
    }

}
@Service
@CacheConfig(cacheNames = "simSlave")
public class SimSlaveServiceImpl implements SimSlaveService {


    @Override
    @Transactional
    @Cacheable(key = "#id")
    public SlaveSimData getSlaveData(int id) {
        SlaveSimData slaveSimData = slaveSimDataMapper.selectByPrimaryKey(id);
        System.out.println("生成数据缓存"+id);
        return slaveSimData;
    }
}

优化

上面的配置方法,如果希望设置时间需要基于 redisCacheConfigurationMap.put,将key与过期时间分离,在这里引用SpringBoot缓存注解@Cacheable之自定义key策略及缓存失效时间指定 - 掘金 (juejin.cn) 自定义的策略如下:

  • value中,等号左边的为cacheName, 等号右边的为失效时间

要实现这个逻辑,可以扩展一个自定义的RedisCacheManager,如

package com.my.equipment.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.Map;


public class TtlRedisCacheManager extends RedisCacheManager {
    public TtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }

    public TtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, String... initialCacheNames) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheNames);
    }

    public TtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, boolean allowInFlightCacheCreation, String... initialCacheNames) {
        super(cacheWriter, defaultCacheConfiguration, allowInFlightCacheCreation, initialCacheNames);
    }

    public TtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
    }

    public TtlRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, boolean allowInFlightCacheCreation) {
        super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations, allowInFlightCacheCreation);
    }


    @Override
    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
        String[] cells = StringUtils.delimitedListToStringArray(name, "=");
        name = cells[0];
        if (cells.length > 1) {
            long ttl = Long.parseLong(cells[1]);
            // 根据传参设置缓存失效时间
            cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl));
        }
        return super.createRedisCache(name, cacheConfig);
    }

}

public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {

        return new TtlRedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                this.getRedisCacheConfigurationWithTtl(-1)
        );
    }
}
@Service
@CacheConfig(cacheNames = "simSlave=3600")
public class SimSlaveServiceImpl implements SimSlaveService {

    @Override
    @Transactional
    @Cacheable(key = "#id")
    public SlaveSimData getSlaveData(int id) {
        SlaveSimData slaveSimData = slaveSimDataMapper.selectByPrimaryKey(id);
        System.out.println("生成数据缓存"+id);
        return slaveSimData;
    }
}

注意@CacheConfig(cacheNames = "simSlave=3600")与 redisCacheConfigurationMap.put("simSlave", this.getRedisCacheConfigurationWithTtl(60 * 60 ));同时使用@CacheConfig(cacheNames = "simSlave=3600")的优先级更高。