重写springboot的缓存管理器,实现spring缓存注解支持自定义有效时间

 1. redis配置代码

package cn.doubi.learn.framework.redis.autoconfigure;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.math.BigDecimal;
import java.math.BigInteger;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * 缓存配置-redis
 * @Author liaoly
 * @Date 2020年8月27日 下午2:20:34
 */
@Configuration
@EnableCaching
@ConditionalOnClass({RedisConnectionFactory.class})
@ConditionalOnProperty(prefix = "spring.redis",name = "host") //没有spring.redis.host 配置项时 不注入容器
@Slf4j
public class RedisConfiguration extends CachingConfigurerSupport {

	/**
	 * Redis缓存配置:配置redisTemplate
	 * @author liaoly
	 * @date 2020年7月31日 下午2:22:45
	 * @return JedisPoolConfig
	 */
	@Bean
	public RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory factory) {

		log.info("配置redisTemplate StringRedisTemplate");

		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

		RedisSerializer<String> redisSerializer = new StringRedisSerializer();

		Jackson2JsonRedisSerializer<?> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

		jackson2JsonRedisSerializer.setObjectMapper(objectMapper());

		redisTemplate.setConnectionFactory(factory);
		//key序列化方式
		redisTemplate.setKeySerializer(redisSerializer);
		//value序列化
		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
		//value hashmap序列化
		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
		redisTemplate.afterPropertiesSet();

		return redisTemplate;
	}

	/**
	 *
	 * Redis缓存配置:5.配置Redis缓存管理器: RedisCacheManager
	 *
	 * @author liaoly
	 * @date 2021年6月8日 上午11:02:47
	 * @param factory redis工厂
	 * @return CacheManager
	 */
	@Bean
	public CacheManager cacheManager(RedisConnectionFactory factory) {

		RedisSerializer<String> redisSerializer = new StringRedisSerializer();
		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		jackson2JsonRedisSerializer.setObjectMapper(objectMapper());

		// 配置序列化(解决乱码的问题)
		RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
				.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
				.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
				.disableCachingNullValues();

		return new TtlRedisCacheManager(RedisCacheWriter.lockingRedisCacheWriter(factory), config);
//		return RedisCacheManager.builder(factory).cacheDefaults(config).build();
	}

	/**
	 * 全局的对象转化json规则自定义
	 * @return {@link ObjectMapper}
	 * @author liaoly
	 * @date 2022/6/27 10:02
	 */
	private ObjectMapper objectMapper() {

		ObjectMapper objectMapper = new ObjectMapper();

		//将Long,BigInteger,BigDecimal序列化的时候,转化为String
		SimpleModule simpleModule = new SimpleModule();
		simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
		simpleModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
		objectMapper.registerModule(simpleModule);
		objectMapper.registerModule(new JavaTimeModule());

		// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
		objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
		// 忽略未知字段
		objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
		// 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
//		objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
		objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance , ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
		// null值、""都不序列化
		objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

		return objectMapper;
	}

	/**
	 * 自定义缓存策略,对于同一业务(同一业务逻辑处理的方法,哪怕是集群/分布式系统),生成的 key 始终一致,对于不同业务则不一致:
	 */
	@Bean
	public KeyGenerator customKeyGenerator() {
		return (o, method, objects) -> {
			StringBuilder sb = new StringBuilder();
			sb.append(o.getClass().getName());
			sb.append(method.getName());
			for (Object obj : objects) {
				sb.append(obj.toString());
			}
			return sb.toString();
		};
	}
}

 2.自定义缓存管理器RedisCacheManager

package cn.doubi.learn.framework.redis.autoconfigure;

import java.time.Duration;
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.lang.Nullable;
import org.springframework.util.StringUtils;

/**
 *
 * 自定义CacheManager, 解析 @Cacheable 支持设置缓存有效时间,写法如下:
 *      <code>@Cacheable(value = "AdminHome=24", key = "#root.methodName")</code> 代表缓存有效期24h
 *
 *  value值单位:d:天;h:小时;m:分钟;s:秒(eg: 1d代表一天;2h代表2个小时;)
 *
 * @author liaoly(廖凌云) [1302013247@qq.com]
 * @date 2022/9/13 15:36
 */
public class TtlRedisCacheManager extends RedisCacheManager {

    //d:天;h:小时;m:分钟;s:秒
    private static final String d = "d";
    private static final String h = "h";
    private static final String m = "m";
    private static final String s = "s";

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

    @Override
    protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
        String[] cells = StringUtils.delimitedListToStringArray(name, "=");
        name = cells[0].trim();
        if (cells.length > 1) {
            cacheConfig = entryTtl(cacheConfig, cells[1].trim().toLowerCase());
        }
        return super.createRedisCache(name, cacheConfig);
    }

    /**
     * 根据传参设置缓存失效时间,并兼容单位(默认单位是h)
     * @param cacheConfig redis缓存配置
     * @param ttlStr 带单位的缓存有效期值 d:天;h:小时;m:分钟;s:秒(eg: 1d代表一天;2h代表2个小时;)
     * @return {@link RedisCacheConfiguration}
     * @author liaoly
     * @date 2023/3/24 8:20
     */
    private RedisCacheConfiguration entryTtl(RedisCacheConfiguration cacheConfig, String ttlStr) {

        ttlStr = ttlStr.toLowerCase();

        if (ttlStr.endsWith(d)) {
            return cacheConfig.entryTtl(Duration.ofDays(Long.parseLong(ttlStr.replace(d, ""))));
        } else if (ttlStr.endsWith(h)) {
            return cacheConfig.entryTtl(Duration.ofHours(Long.parseLong(ttlStr.replace(h, ""))));
        } else if (ttlStr.endsWith(m)) {
            return cacheConfig.entryTtl(Duration.ofMinutes(Long.parseLong(ttlStr.replace(m, ""))));
        } else if (ttlStr.endsWith(s)) {
            return cacheConfig.entryTtl(Duration.ofSeconds(Long.parseLong(ttlStr.replace(s, ""))));
        } else {
            return cacheConfig.entryTtl(Duration.ofHours(Long.parseLong(ttlStr.replaceAll("[^\\d]", ""))));
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
非常感谢您的提问。以下是 Spring Boot Mybatis Plus 使用 Redis 实现二级缓存的具体步骤和代码: 1. 首先,在 pom.xml 文件中添加 Redis 相关依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency> ``` 2. 在 application.properties 文件中添加 Redis 相关配置: ``` spring.redis.host=127.0.0.1 spring.redis.port=6379 spring.redis.database=0 spring.redis.password= spring.redis.timeout=3000 spring.redis.jedis.pool.max-active=8 spring.redis.jedis.pool.max-wait=-1 spring.redis.jedis.pool.max-idle=8 spring.redis.jedis.pool.min-idle=0 ``` 3. 在 Mybatis Plus 的配置文件中开启二级缓存,并配置 Redis 缓存: ``` @Configuration @MapperScan("com.example.mapper") public class MybatisPlusConfig { @Bean public ConfigurationCustomizer configurationCustomizer() { return new ConfigurationCustomizer() { @Override public void customize(Configuration configuration) { // 开启二级缓存 configuration.setCacheEnabled(true); // 配置 Redis 缓存 RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30)); // 设置缓存过期时间为 30 分钟 configuration.addCache(new RedisCache("mybatis-plus", new RedisCacheWriter() { @Override public void put(String key, byte[] value) { redisTemplate().opsForValue().set(key, value, Duration.ofMinutes(30)); } @Override public byte[] get(String key) { return redisTemplate().opsForValue().get(key); } @Override public void put(String key, byte[] value, long time, TimeUnit unit) { redisTemplate().opsForValue().set(key, value, Duration.ofMillis(unit.toMillis(time))); } @Override public void delete(String key) { redisTemplate().delete(key); } @Override public void clean() { redisTemplate().getConnectionFactory().getConnection().flushDb(); } @Override public long size() { return redisTemplate().getConnectionFactory().getConnection().dbSize(); } }, redisCacheConfiguration)); } }; } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); return redisTemplate; } @Bean public RedisConnectionFactory redisConnectionFactory() { LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(); lettuceConnectionFactory.setHostName("127.0.0.1"); lettuceConnectionFactory.setPort(6379); lettuceConnectionFactory.setPassword(""); lettuceConnectionFactory.setDatabase(0); return lettuceConnectionFactory; } } ``` 4. 在需要使用二级缓存的 Mapper 中添加 @CacheNamespace 注解: ``` @CacheNamespace(implementation = MybatisRedisCache.class, eviction = MybatisRedisCache.class) public interface UserMapper extends BaseMapper<User> { // ... } ``` 5. 最后,实现 MybatisRedisCache 类,继承自 RedisCache重写 clear 方法: ``` public class MybatisRedisCache extends RedisCache { public MybatisRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration configuration) { super(name, cacheWriter, configuration); } @Override public void clear() { RedisConnection connection = Objects.requireNonNull(getRedisCacheWriter().getRedisConnectionFactory().getConnection()); connection.flushDb(); connection.close(); } } ``` 以上就是 Spring Boot Mybatis Plus 使用 Redis 实现二级缓存的具体步骤和代码。希望能对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值