SpringCache 自定义过期时间、序列化方式

一些Maven

    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.31</version>
    </dependency>
    
    <!-- Spring Cache 在这里面包含了 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.10</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

这里面自定义cacheManager,配置序列化方式、自动获取并设置ttl

import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.cache.*;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.lang.Nullable;

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

/**
 * @author fly_roushan
 * 2023/5/11 16:25
 */
public class CustomizerRedisCacheManager extends RedisCacheManager {
	// 自定义ttl的分隔符
    private static final String DEFAULT_SEPARATOR = "#";
	// 用来保存返回值信息和注解,方便获取所需信息及自定义value的序列化
    private final Map<String, CacheInfo> typeMap;
	// 默认的key的序列化方式
    private static final RedisSerializationContext.SerializationPair<String> DEFAULT_KEY_SERIALIZATION = RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer());

    public CustomizerRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, CacheInfo> typeMap) {
        super(cacheWriter, defaultCacheConfiguration);
        this.typeMap = typeMap;
    }

    @Override
    protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
        if (cacheConfig == null) {
            return super.createRedisCache(name, null);
        }
		// 获取并设置ttl
        Duration ttl = getTtlByName(name);
        if (ttl != null) {
            cacheConfig = cacheConfig.entryTtl(ttl);
        }
        // 设置key前缀,没有key时不使用:
        // 默认生成的key时这样, cacheName::key,很难看,而且会出现空目录
        // 现在有key就是, cacheName:key
        // 没有key就是, cacheName, 不会有额外的冒号
        CacheInfo info = typeMap.get(name);
        if (StringUtils.isNotBlank(info.getCacheable().key())) {
            cacheConfig = cacheConfig.computePrefixWith(DEFAULT_CACHE_KEY_PREFIX);
        } else {
            cacheConfig = cacheConfig.computePrefixWith(cacheName -> cacheName);
        }
        cacheConfig = cacheConfig
                .serializeKeysWith(DEFAULT_KEY_SERIALIZATION)
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new FastJsonRedisSerializer<>(info.getReturnType())));
        // 去除最终生成的key中用来设置ttl的字符
        return super.createRedisCache(name.substring(0, name.indexOf(DEFAULT_SEPARATOR)), cacheConfig);
    }

    private static final CacheKeyPrefix DEFAULT_CACHE_KEY_PREFIX = cacheName -> cacheName + ":";

    private Duration getTtlByName(String name) {
        if (name == null) {
            return null;
        }

        String[] cacheParams = name.split(DEFAULT_SEPARATOR);
        if (cacheParams.length > 1) {
            String seconds = cacheParams[1];
            if (StringUtils.isNotBlank(seconds)) {
                return Duration.ofSeconds(Long.parseLong(seconds));
            }
        }

        return null;
    }
}

Redis序列化类,使用FastJson序列化

import com.alibaba.fastjson2.JSON;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.lang.reflect.Type;

/**
 * @author fly_roushan
 * 2023/4/10 14:06
 */
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
    private final Type type;

    public FastJsonRedisSerializer(Type type) {
        this.type = type;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        return JSON.toJSONBytes(t);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        return JSON.parseObject(bytes, type);
    }
}

用来临时保存注解信息和返回类型

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.cache.annotation.Cacheable;

import java.lang.reflect.Type;

/**
 * @author fly_roushan
 * 2023/5/12 17:17
 */
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class CacheInfo {

    /**
     * 返回值类型
     */
    private Type returnType;

    /**
     * 注解信息
     */
    private Cacheable cacheable;
}

Redis配置,自定义keyGenerate,不建议使用keyGenerate,命名不清晰,没有逻辑

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * @author fly_roushan
 * 2023/5/10 15:35
 */
@Configuration
@EnableCaching(proxyTargetClass = true)
public class SpringCacheConfig extends CachingConfigurerSupport {

    @Resource
    private ApplicationContext applicationContext;

    /**
     * SpringCache默认设置为有锁写入
     * 可根据cacheName自定义过期时间
     * cacheName = "cacheName#60"
     * #后为值,单位为每秒
     *
     * @param redisConnectionFactory factory
     * @return cacheManager
     */
    @Bean
    public CacheManager shortCacheManager(RedisConnectionFactory redisConnectionFactory) {
        return new CustomizerRedisCacheManager(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory), RedisCacheConfiguration.defaultCacheConfig(), initTypeMap());
    }

    /**
     * 指定固定key,不要依赖生成器
     *
     * @return key generator
     */
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> "";
    }

	// 获取使用@Cacheable的方法信息
    private Map<String, CacheInfo> initTypeMap() {
        Map<String, CacheInfo> cacheConfigMap = new HashMap<>(32);
        Arrays.stream(applicationContext.getBeanNamesForType(Object.class))
                .map(applicationContext::getType).filter(Objects::nonNull)
                .forEach(clazz -> ReflectionUtils.doWithMethods(clazz, method -> {
                            ReflectionUtils.makeAccessible(method);
                            Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);
                            if (Objects.nonNull(cacheable)) {
                                for (String cache : cacheable.cacheNames()) {
                                    cacheConfigMap.put(cache, new CacheInfo(method.getGenericReturnType(), cacheable));
                                }
                            }
                        })
                );
        return cacheConfigMap;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值