springcache 整合redis 动态配置过期时间

使用

  1. 引入pom文件
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
  1. 添加注解 @EnableCaching

注解使用

注解参数含义

  1. value . 缓存的名称。必须指定至少一个。和cacheNames 一样
  2. cacheNames 缓存的名称。必须指定至少一个。和value 一样
  3. key 缓存的key,可以为空。如果指定要按SPEL表达式编写。不指定会按照入参自行组合
  4. cacheManager 指定管理器
  5. condition 缓存的条件,可以为空,使用SPEL表达式编写,返回true或者false,true表示存入缓存

@Cacheable

添加缓存
使用在方法上,指定cacheNames 意思是返回值存入的缓存名称。下次有请求则先去缓存中查看是否有key,key可以自行指定,如果不指定会判断所有入参,如果不同就认为是不同的key。
如果不指定key,则默认全部参数作为key。
如果是自定义对象类型,判断不了需要自定义KeyGenerator

@Component
public class MyGenerator implements KeyGenerator{
	@override
	public Object generate(object o, Method method, object... objects){
 		
	}
}

@CacheEvict

清除缓存
用法和cacheable一样,只是删除缓存

@CachePut

更新缓存
每次都会调用方法,把返回值缓存起来

@cacheManager

管理cache 的类。可以配置多个,配置多个的时候需要在默认的bean上添加@primary

 @Bean
 @Primary
 public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
 //初始化一个RedisCacheWriter
 RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
 //设置CacheManager的值序列化方式为json序列化
 RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
 RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);
  //设置默认超过时期是1天
 RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair)
 .entryTtl(Duration.ofDays(1));
 //初始化RedisCacheManager
 return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
 }

@bean
public ConcurrentMapCacheMamager cacheManager(){
	return new ConcurrentMapCacheManager();
}

原理

SpringCache包含两个顶级接口,Cache(缓存)和CacheManager(缓存管理器),顾名思义,用CacheManager去管理一堆Cache。

@Cacheable 中 指定的 cacheNames 其实就是 指向了一个个实现了 Cache 接口的类,我们也可以自定义MyCache 实现 Cache 接口然后使用。

RedisCacheManager

存储情况在redisCache中体现。
cacheWriter中获得存在Redis中的存储的key
defaultrediscachewriter 中是真正和redis交互的类
存储在redis中的key格式为 cacheName::key.

自定义redis过期时间

在创建rediscachemanager 的时候,还可以传入一个config,是特定cacheName 对应的配置文件

Map<string,redisCacheConfiguration> conf;
new RedisCacheManager(redisCacheWriter, defaultCacheConfig,conf);

思路: 这样还是属于硬编码。想要达到的效果是在 @cacheable 的时候直接能够指定过期时间。
通过利用cachename的方式,把过期时间写到cachename里面,在生成配置文件

代码实现

在 @cacheable 中定义名称为 @Cacheable(cacheNames = "caheame#10s") 用#分割,然后 s m d 设置

package com.meiya.config;

import com.alibaba.fastjson.serializer.MapSerializer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

@Configuration

public class CacheConfig {


    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    @Primary
    RedisCacheManager redisCacheManager() {
        final RedisCacheWriter writer = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
        final GenericJackson2JsonRedisSerializer jackson = new GenericJackson2JsonRedisSerializer();
        final RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                .fromSerializer(jackson);
        final RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(pair).entryTtl(Duration.ofSeconds(30));
        final Map<String, RedisCacheConfiguration> conf = getConf();
        System.out.println(conf);
        return new RedisCacheManager(writer, config, conf);
    }


    @Bean
    public ConcurrentMapCacheManager cacheManager() {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        return cacheManager;
    }


    private Map<String, RedisCacheConfiguration> getConf() {

        final Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(Component.class);
        final List<CacheMap> collect = beansWithAnnotation.entrySet().stream().flatMap(entry -> {
            try {
                final Object value = entry.getValue();
                // 获得原本的class名字,spring代理的都是后面有$$直接截取即可
                String className = "";
                if (value.getClass().getName().contains("$")){
                    className = value.getClass().getName().
                            substring(0, value.getClass().getName().indexOf("$"));
                }else{
                    className = value.getClass().getName();
                }

                // 获得原始的字节码文件,如果被spring 代理之后,方法上会获取不到注解信息
                final Method[] methods = Class.forName(className)
                        .getDeclaredMethods();

                return Arrays.stream(methods).flatMap(method -> {
                    final Cacheable annotation = method.getAnnotation(Cacheable.class);
                    if (annotation == null) {
                        return null;
                    }
                    return Arrays.stream(annotation.cacheNames()).map(data -> {
                        if (data.contains("#")) {
                            // 包含自定义日期
                            final String[] split = data.split("#");
                            if (split.length != 2) {
                                return null;
                            }
                            final CacheMap cacheMap = new CacheMap();
                            cacheMap.setName(data);
                            final String s = split[1];
                            final int time = Integer.parseInt(s.substring(0, s.length() - 1));
                            if (s.endsWith("s")) {
                                cacheMap.setTtl(Duration.ofSeconds(time));
                            } else if (s.endsWith("m")) {
                                cacheMap.setTtl(Duration.ofMinutes(time));
                            } else if (s.endsWith("d")) {
                                cacheMap.setTtl(Duration.ofDays(time));
                            }
                            return cacheMap;
                        }
                        return null;
                    }).filter(Objects::nonNull);
                });


            } catch (Exception e) {
                System.out.println("异常");
                return null;
            }
        }).collect(Collectors.toList());

        return collect.stream().collect(Collectors.toMap(CacheMap::getName, p -> {
            final GenericJackson2JsonRedisSerializer jackson = new GenericJackson2JsonRedisSerializer();
            final RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
                    .fromSerializer(jackson);
            return RedisCacheConfiguration.defaultCacheConfig()
                    .serializeValuesWith(pair).entryTtl(p.getTtl());
        }, (key1, key2) -> key2));
    }


    class CacheMap {
        private String name;
        private Duration ttl;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Duration getTtl() {
            return ttl;
        }

        public void setTtl(Duration ttl) {
            this.ttl = ttl;
        }
    }


}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值