spring boot spring cache 使用redis多缓存策略的实现

​在项目中对数据的访问往往都是直接访问数据库的方式,但如果对数据的访问量很大或者访问很频繁的话,将会对数据库来很大的压力,甚至造成数据库崩溃。为了解决这类问题大家都会在项目中加入缓存。还有一种情况也是比较适合增加缓存的,数据的更新周期较长,很长时间不会改变。

对于springboot 项目,spring官方提供的默认缓存方案只支持一种缓存策略,既不同的缓存过期时间一致。因项目实际需要不同缓存不同的过期时间,所以对多缓存策略进行了研究。这里采用redis作为缓存容器,至于原因不再累赘。详细过程如下

1. 安装redis数据库

2. 新建spring boot  2.1.7项目,pom文件如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>redis-cache</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>redis-cache</name>
<description>Demo project for Spring Boot</description>

<properties>
    <java.version>1.8</java.version>
</properties>

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

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

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
</dependencies>

 

3.启动类增加@EnableCaching注解。

4.自定义配置文件读取,代码如下:

@ConfigurationProperties(prefix = "caching")
@Component
@Data
public class MyCacheProperties {

    private List<CacheSpec> specs;
    @Data
    static class CacheSpec {

        /**
         * 缓存过期时间
         */
        private Integer timeout;

        /**
         * 缓存名称
         */
        private String cacheName;
    }
}

5. 自定义redis配置

@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
public class RedisConfig {


    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, MyCacheProperties properties,
                                          CacheProperties cacheProperties) {

        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(defaultCacheConfig(cacheProperties));

        //自定义配置
        List<MyCacheProperties.CacheSpec> cacheSpecs = properties.getSpecs();
        if (!CollectionUtils.isEmpty(cacheSpecs)) {
            Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
            cacheSpecs.forEach(cacheSpec -> {
                RedisCacheConfiguration config = defaultCacheConfig(cacheProperties);
                if (cacheSpec.getTimeout() != null && StringUtils.isNoneEmpty(cacheSpec.getCacheName())) {
                    config = config.entryTtl(Duration.ofSeconds(cacheSpec.getTimeout()));
                    cacheConfigurations.put(cacheSpec.getCacheName(), config);
                }
            });
            builder = builder.withInitialCacheConfigurations(cacheConfigurations).transactionAware();
        }
        List<String> cacheNames = cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
        }
        return builder.build();
    }

    /**
     * 默认缓存配置,使用spring cache 默认配置 也可在配置文件中修改spring cache缓存策略
     */
    private RedisCacheConfiguration defaultCacheConfig(CacheProperties cacheProperties) {
        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()));
        if (redisProperties.getTimeToLive() != null) {
            config = config.entryTtl(redisProperties.getTimeToLive());
        }
        if (redisProperties.getKeyPrefix() != null) {
            config = config.prefixKeysWith(redisProperties.getKeyPrefix());
        }
        if (!redisProperties.isCacheNullValues()) {
            config = config.disableCachingNullValues();
        }
        if (!redisProperties.isUseKeyPrefix()) {
            config = config.disableKeyPrefix();
        }
        return config;
    }


    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        redisTemplate.setKeySerializer(keySerializer());
        redisTemplate.setHashKeySerializer(keySerializer());
        redisTemplate.setValueSerializer(valueSerializer());
        redisTemplate.setHashValueSerializer(valueSerializer());
        return redisTemplate;
    }

    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    /**
     * 指定redis value的序列化方式为jackson
     *
     */
    private RedisSerializer<Object> valueSerializer() {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        //设置输入时忽略JSON字符串中存在而Java对象实际没有的属性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        JavaTimeModule module = new JavaTimeModule();
        module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        objectMapper.registerModule(module);
        objectMapper.registerModule(new Jdk8Module());
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }



    /**
     * 自定义缓存生成key
     */
    @Bean
    public KeyGenerator mKeyGenerator() {
        return (Object target, Method method, Object... params) -> {
            String key = target.getClass().getName().concat("|").concat(method.getName());
            StringBuilder stringBuilder = new StringBuilder(key);
            for (Object param : params) {
                if (!param.getClass().getName().startsWith("org.springframework")) {
                    stringBuilder.append("|");
                    stringBuilder.append(param.toString());
                }
            }
            return stringBuilder.toString();
        };
    }
}

 

6.新建测试controller 

@RestController
public class CacheTestController {


    @Cacheable(cacheNames = "test1", key = "method.name")
    @GetMapping("/test1")
    public Map<String, String> test1(HttpServletRequest request) {
        return getCacheOrMap(request);
    }

    @Cacheable(cacheNames = "test2", keyGenerator = "mKeyGenerator")
    @GetMapping("/test2")
    public Map<String, String> test2(HttpServletRequest request) {
        return getCacheOrMap(request);
    }

    private Map<String, String> getCacheOrMap(HttpServletRequest request) {
        request.getMethod();
        Map<String, String> result = new HashMap<>();
        result.put("method", request.getMethod());
        result.put("time", DateTimeFormatter.ISO_DATE_TIME.format(LocalDateTime.now()));
        return result;
    }

}

 

配置文件application.yml如下

server:
  port: 8090
spring:
  application:
    name: redis-cache
  redis:
    host: 127.0.0.1
    port: 6379
  cache:
#    指定缓存为redis
    type: redis
caching:
  specs:
      #  test1 缓存过期时间60s
    - cacheName: test1
      timeout: 60
      #  test2 缓存过期时间10s
    - cacheName: test2

 

至此功能已经实现,访问对应的接口即可进行测试。代码地址: 

https://github.com/lbovery/redis-cache

其实相对来说没那么复杂,要实现这种非官方的自定义功能,建议大家多看源码,了解spirng boot的原理,这样以后不管遇到什么问题都能迎刃而解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值