SpringBoot 2.x 整合 redis 做缓存,并支持自定义过期时间

SpringBoot 2.x 整合 redis 做缓存,并支持自定义过期时间

SpringBoot 2.2.0是最新版本,至少在我写这篇文章的此时此刻,为啥用最新版本?因为最新版本的改动相较于2.2.0之前的版本对于redis还是有些改动,所以上新版本之前大伙要注意一下这个问题,我们使用redis接管spring的缓存机制,用于直接使用@Cacheable标记来缓存结果,并随意设定过期时间。

一、redis的相关配置

1、引入spring-redis的依赖

<!-- spring boot redis集成 -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- spring boot redis连接池集成 -->
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-pool2</artifactId>
</dependency>

2、配置redis数据库

redis:
    database: 3
    host: tst.mindmedia.cn
    port: 6379
    password: M1ndmed1a
    timeout: 60000
    lettuce: #由于Spring Boot2.x 的改动,连接池相关配置需要通过spring.redis.lettuce.pool或者 spring.redis.jedis.pool 进行配置
      pool:
        max-active: 200 #连接池最大连接数(使用负值表示没有限制) 默认8
        max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
        min-idle: 0 #连接池中的最小空闲连接
        max-idle: 10 #连接池中的最大空闲连接

3、配置CacheManager

 /**
  * redis配置类
 *
 * @author guoyong
 * @date 0001/2019-11-11 上午 11:48
 */
@Configuration
@EnableCaching
 public class RedisCacheConfig extends CachingConfigurerSupport {
 /**
	 * 默认缓存的过期时间,该过期时间将作用于没有指定过期时间的缓存上
	 */
	private static final Duration timeToLive = Duration.ofHours(1);


	@Bean(name = "redisTemplate")
	public RedisTemplate<String, Object redisTemplate(RedisConnectionFactory redisConnectionFactory) {
		RedisTemplate<String, Object template = new RedisTemplate<();
		template.setConnectionFactory(redisConnectionFactory);
		StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
		// key采用String的序列化方式
		template.setKeySerializer(stringRedisSerializer);
		// hash的key也采用String的序列化方式
		template.setHashKeySerializer(stringRedisSerializer);
		// value序列化方式采用jdk提供的
		template.setValueSerializer(new JdkSerializationRedisSerializer());
		// hash的value序列化方式采用jdk提供的
		template.setHashValueSerializer(new JdkSerializationRedisSerializer());
		template.afterPropertiesSet();
		return template;
	}

	@Bean
	public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
		RedisSerializer<String redisSerializer = new StringRedisSerializer();
		Jackson2JsonRedisSerializer<Object jackson2JsonRedisSerializer = getJackson2JsonRedisSerializer();
		// 配置序列化(解决乱码的问题).disableCachingNullValues() 是否允许缓存空值
		RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
				.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
				.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
				.computePrefixWith(keyPrefix()).entryTtl(timeToLive);
		RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
		return new RedisAutoCacheManager(redisCacheWriter, redisCacheConfiguration);
	}

	private Jackson2JsonRedisSerializer<Object getJackson2JsonRedisSerializer() {
		Jackson2JsonRedisSerializer<Object jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<(Object.class);
		//解决查询缓存转换异常的问题
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);
		return jackson2JsonRedisSerializer;
	}

	/**
	 * 缓存前缀(追加一个冒号 : )官方的keyPrefix为给key值末尾追加两个冒号,不知何故,所以这里自定义一下
	 *
	 * @return
	 */
	private CacheKeyPrefix keyPrefix() {
		return name - name + ":";
	}
}

/**
 * 重载RedisCacheManager就为了自定义缓存的过期时间
 *
 * @author guoyong
 * @date 0001/2019-11-11 上午 11:54
 */
public class RedisAutoCacheManager extends RedisCacheManager {

	private static final String SPLIT_FLAG = "#";
	private static final int CACHE_LENGTH = 2;

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

	@Override
	protected Collection<RedisCache loadCaches() {
		return super.loadCaches();
	}

	@Override
	protected RedisCache getMissingCache(String name) {
		return super.getMissingCache(name);
	}

	@Override
	public Map<String, RedisCacheConfiguration getCacheConfigurations() {
		return super.getCacheConfigurations();
	}

	@Override
	protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
		if (StringUtils.isBlank(name) || !name.contains(SPLIT_FLAG)) {
			return super.createRedisCache(name, cacheConfig);
		}
		String[] cacheArray = name.split(SPLIT_FLAG);
		if (cacheArray.length < CACHE_LENGTH) {
			return super.createRedisCache(name, cacheConfig);
		}
		if (cacheConfig != null) {
			long cacheAge = Long.parseLong(cacheArray[1]);
			name = name.substring(0, name.lastIndexOf(SPLIT_FLAG));
			cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(cacheAge));
		}
		return super.createRedisCache(name, cacheConfig);
	}

	@Override
	public void setTransactionAware(boolean transactionAware) {
		super.setTransactionAware(transactionAware);
	}

	@Override
	public boolean isTransactionAware() {
		return super.isTransactionAware();
	}

	@Override
	protected Cache decorateCache(Cache cache) {
		return super.decorateCache(cache);
	}

	@Override
	public void afterPropertiesSet() {
		super.afterPropertiesSet();
	}

	@Override
	public void initializeCaches() {
		super.initializeCaches();
	}

	@Override
	public Cache getCache(String name) {
		return super.getCache(name);
	}

	@Override
	public Collection<String getCacheNames() {
		return super.getCacheNames();
	}

	@Override
	public int hashCode() {
		return super.hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		return super.equals(obj);
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}

	@Override
	public String toString() {
		return super.toString();
	}

	@Override
	protected void finalize() throws Throwable {
		super.finalize();
	}
}
排版真的一件令人痛苦的事情,从事程序员这个行业这么久,从CSDN上看了无数的文章,今天终于还是忍不住把最近对springboot 2相关的研究发上来跟大家交流,有问题随时找我沟通,在放1个使用示例
/**
 * 用户信息缓存24小时,redis过期时间为秒
 */
private static final String MP_KEY_MEM_APP = "MP:KEY:MEM:APP#" + 24 * 3600;

@Autowired
private MpMemberMapper mpMemberMapper;

@Cacheable(value = MP_KEY_MEM_APP, key = "#mpAppId+':'+#openId")
public MpMember getMpMember(String mpAppId, String openId) {
	MpMember mpMemberSearch = new MpMember();
	mpMemberSearch.setMpAppId(mpAppId);
	mpMemberSearch.setOpenId(openId);
	log.debug("getMpMember by Db mpAppId {} openId {}", mpAppId, openId);
	return mpMemberMapper.selectOne(mpMemberSearch);
}

@CacheEvict(value = MP_KEY_MEM_APP, key = "#mpMember.mpAppId+':'+#mpMember.openId")
public int insert(MpMember mpMember) {
	mpMember.initInsert();
	return mpMemberMapper.insert(mpMember);
}

@CacheEvict(value = MP_KEY_MEM_APP, key = "#mpMember.mpAppId+':'+#mpMember.openId")
public int updateByPrimaryKeySelective(MpMember mpMember) {
	mpMember.initUpdate();
	return mpMemberMapper.updateByPrimaryKeySelective(mpMember);
}

集成时碰到的问题

// value序列化方式采用jdk提供的
template.setValueSerializer(new JdkSerializationRedisSerializer());
// hash的value序列化方式采用jdk提供的
template.setHashValueSerializer(new JdkSerializationRedisSerializer());

这两个值的序列化方式过变更为Jackson2JsonRedisSerializer的话,使用@Cacheable标记缓存的结果将无法被正常读取出来,会抛解析异常,具体机理我没有仔细研究,有知道的小伙伴欢迎diss我,然后告诉我具体原因

RedisCacheConfiguration.disableCachingNullValues() 

是否允许缓存空值,这个值需要特别关注一下,如果启用了,会导致使用@Cacheable缓存标记时会缓存空值,可能会导致非预期的结果,因为有些业务因为时效性的原因是不允许缓存空结果的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值