1. 前言

上一文我们整合了Mybatis Plus,今天我们会把缓存也集成进来。缓存是一个系统应用必备的一种功能,除了在减轻数据库的压力之外。还在存储一些短时效的数据场景中发挥着重大作用,比如存储用户Token、短信验证码等等,目前缓存的选型还是比较多的,EHCACHEHAZELCASTCAFFEINECOUCHBASE以及本文要整合的REDIS。接下来我们将会在kono脚手架项目中集成Spring Cache以及Redis

Gitee: https://gitee.com/felord/kono day05 分支

GitHub: https://github.com/NotFound403/kono day05 分支

2. 整合目标

使项目具有缓存功能,同时将默认的JDK序列化修改为Jackson序列化以存储一些对象,同时实现一些特定的个性化的缓存空间以满足不同场景下的不同缓存TTL时间需求。

3. 依赖集成

目前只需要引入下面的依赖即可:

 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

默认情况下spring-data-redis使用高性能的lettuce客户端实现,当然你可以替换为老旧的jedis

4. 缓存及Redis配置

缓存以及Redis相关的配置项分别为spring.cachespring.redis开头的配置,这里比较简单的配置为:

spring:
redis:
host: localhost
port: 6379
cache:
# type: REDIS
redis:
# 全局过期时间
time-to-live: 120

5. RedisTemplate个性化

默认情况下会有两个模板类被注入Spring IoC供我们使用,需要个性化配置来满足实际的开发。

一个是RedisTemplate<Object, Object>,主要用于对象缓存,其默认使用JDK序列化,我们需要更改其序列化方式解决一些问题,比如Java 8日期问题、JSON序列化问题。需要我们重写一下。

/**
* Redis的一些自定义配置.
*
* @author felord.cn
* @since 2020 /8/17 20:39
*/
@ConditionalOnClass(ObjectMapper.class)
@Configuration(proxyBeanMethods = false)
public class RedisConfiguration {
/**
* Redis template redis template.
*
* @param redisConnectionFactory the redis connection factory
* @return the redis template
*/
@Bean("redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = initJacksonSerializer();
// 设置value的序列化规则和 key的序列化规则
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setKeySerializer(new StringRedisSerializer());
template.afterPropertiesSet();
return template;
} /**
* 处理redis序列化问题
* @return Jackson2JsonRedisSerializer
*/
private Jackson2JsonRedisSerializer<Object> initJacksonSerializer() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//以下替代旧版本 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
//bugFix Jackson2反序列化数据处理LocalDateTime类型时出错
om.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
// java8 时间支持
om.registerModule(new JavaTimeModule());
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
} }

另一个是StringRedisTemplate,主要处理键值都是字符串的缓存,采用默认就好。

6. 缓存个性化

使用Spring Cache做缓存的时候,有针对不同的key设置不同过期时间的场景。比如Jwt Token我想设置为一周过期,而验证码我想设置为五分钟过期。这个怎么实现呢?需要我们个性化配置RedisCacheManager。首先我通过枚举来定义这些缓存及其TTL时间。例如:

/**
* 缓存定义枚举
*
* @author felord.cn
* @see cn.felord.kono.configuration.CacheConfiguration
* @since 2020/8/17 21:40
*/ public enum CacheEnum { /**
* 用户jwt token 缓存空间 ttl 7天
*/
JWT_TOKEN_CACHE("usrTkn", 7 * 24 * 60 * 60),
/**
* 验证码缓存 5分钟ttl
*/
SMS_CAPTCHA_CACHE("smsCode", 5 * 60); /**
* 缓存名称
*/
private final String cacheName;
/**
* 缓存过期秒数
*/
private final int ttlSecond; CacheEnum(String cacheName, int ttlSecond) {
this.cacheName = cacheName;
this.ttlSecond = ttlSecond;
} public String cacheName() {
return this.cacheName;
} public int ttlSecond() {
return this.ttlSecond;
}
}

这样就能很清楚地描述个性化的缓存了。

然后我们通过向Spring IoC分别注入RedisCacheConfigurationRedisCacheManagerBuilderCustomizer 来个性化配置,你可以留意CacheEnum是如何工作的。如果你有其它的个性化需要也可以对这两个配置类进行定制化。

import cn.felord.kono.enumeration.CacheEnum;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.cache.annotation.EnableCaching;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext; import java.time.Duration;
import java.util.EnumSet;
import java.util.stream.Collectors; /**
* redis 缓存配置.
*
* @author felord.cn
* @since 2020 /8/17 20:14
*/
@EnableCaching
@Configuration
public class CacheConfiguration { /**
* Redis cache configuration.
*
* @param redisTemplate the redis template
* @return the redis cache configuration
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration(RedisTemplate<Object, Object> redisTemplate, CacheProperties cacheProperties) {
// 参见 spring.cache.redis
CacheProperties.Redis redisProperties = cacheProperties.getRedis(); RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
// 缓存的序列化问题
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(redisTemplate.getValueSerializer())); if (redisProperties.getTimeToLive() != null) {
// 全局 TTL 时间
redisCacheConfiguration = redisCacheConfiguration.entryTtl(redisProperties.getTimeToLive());
}
if (redisProperties.getKeyPrefix() != null) {
// key 前缀值
redisCacheConfiguration = redisCacheConfiguration.prefixCacheNameWith(redisProperties.getKeyPrefix());
}
if (!redisProperties.isCacheNullValues()) {
// 默认缓存null值 可以防止缓存穿透
redisCacheConfiguration = redisCacheConfiguration.disableCachingNullValues();
}
if (!redisProperties.isUseKeyPrefix()) {
// 不使用key前缀
redisCacheConfiguration = redisCacheConfiguration.disableKeyPrefix();
}
return redisCacheConfiguration;
} /**
* Redis cache manager 个性化配置缓存过期时间.
* @see RedisCacheManager,CacheEnum
* @return the redis cache manager builder customizer
*/
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(RedisCacheConfiguration redisCacheConfiguration) { return builder -> builder.cacheDefaults(redisCacheConfiguration)
// 自定义的一些缓存配置初始化 主要是特定缓存及其ttl时间
.withInitialCacheConfigurations(EnumSet.allOf(CacheEnum.class).stream()
.collect(Collectors.toMap(CacheEnum::cacheName,
cacheEnum -> redisCacheConfiguration.entryTtl(Duration.ofSeconds(cacheEnum.ttlSecond())))));
} }

个性化的同时我们可以通过注解@EnableCaching开启Spring Cache缓存支持。关于Spring Cache的细节可以通过文章Spring Cache详解来了解。

请注意,只有通过Spring Cache操作缓存才会达到上图的效果。命令行操作需要显式的声明指令。

7. 总结

最近事情比较多,所以难得抽出时间来搞一搞。如果你在实际开发中遇到需要整合的功能也可以告诉我,同时如果你发现整合中的一些缺陷或者Bug请提交ISSUE。多多关注:码农小胖哥,跟我一起整合开发脚手架。

关注公众号:Felordcn 获取更多资讯

个人博客:https://felord.cn

从零搭建Spring Boot脚手架(6):整合Redis作为缓存的更多相关文章

  1. 从零搭建Spring Boot脚手架(7):整合OSS作为文件服务器

    1. 前言 文件服务器是一个应用必要的组件之一.最早我搞过FTP,然后又用过FastDFS,接私活的时候我用MongoDB也凑合凑合.现如今时代不同了,开始流行起了OSS. Gitee: https: ...

  2. 从零搭建Spring Boot脚手架(1):开篇以及技术选型

    1. 前言 目前Spring Boot已经成为主流的Java Web开发框架,熟练掌握Spring Boot并能够根据业务来定制Spring Boot成为一个Java开发者的必备技巧,但是总是零零碎碎 ...

  3. 从零搭建Spring Boot脚手架(2):增加通用的功能

    1. 前言 今天开始搭建我们的kono Spring Boot脚手架,首先会集成Spring MVC并进行定制化以满足日常开发的需要,我们先做一些刚性的需求定制,后续再补充细节.如果你看了本文有什么问 ...

  4. 从零搭建Spring Boot脚手架(3):集成mybatis

    1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文集成了一些基础的功能,比如统一返回体.统一异常处理.快速类型转换.参数校验等常用必备功能,并编写了一些单元测试进行验证,今天 ...

  5. 从零搭建Spring Boot脚手架(4):手写Mybatis通用Mapper

    1. 前言 今天继续搭建我们的kono Spring Boot脚手架,上一文把国内最流行的ORM框架Mybatis也集成了进去.但是很多时候我们希望有一些开箱即用的通用Mapper来简化我们的开发.我 ...

  6. 从零搭建Spring Boot脚手架(7):Elasticsearch应该独立服务

    1. Spring Data Elasticsearch Spring Data Elasticsearch是Spring Data项目的子项目,提供了Elasticsearch与Spring的集成. ...

  7. 从零搭建Spring Boot脚手架(5):整合 Mybatis Plus

    1. 前言 在上一文中我根据Mybatis中Mapper的生命周期手动实现了一个简单的通用Mapper功能,但是遗憾的是它缺乏实际生产的检验.因此我选择更加成熟的一个Mybatis开发增强包.它就是已 ...

  8. Spring Boot 2.x 整合 Redis最佳实践

    一.前言 在前面的几篇文章中简单的总结了一下Redis相关的知识.本章主要讲解一下 Spring Boot 2.0 整合 Redis.Jedis 和 Lettuce 是 Java 操作 Redis 的 ...

  9. Spring Boot 2.x整合Redis

    最近在学习Spring Boot 2.x整合Redis,在这里和大家分享一下,希望对大家有帮助. Redis是什么 Redis 是开源免费高性能的key-value数据库.有以下的优势(源于Redis ...

随机推荐

  1. Vue+ElementUI搭建一个后台管理框架

    参考 :https://www.cnblogs.com/taotaozhuanyong/p/11903750.html https://gitee.com/qianhongtang-share/vue ...

  2. String字符串缓冲区、StringBuffer

    String字符串缓冲区 1.StringBuffer类 StringBuffer又称为可变字符序列,字符串缓冲区支持可变的字符串, StringBuffer是个字符串的缓冲区,即就是它是一个容器,容 ...

  3. 页面上怎么使用svg

    svg标签直接在页面使用 不多说. 其他标签使用svg 除了直接使用svg标签,还有如下方法: <object data="your.svg" type="imag ...

  4. MySQL索引介绍和实战

    索引是什么 MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构. 可以得到索引的本质:索引是数据结构,索引的目的是提高查询效率,可以类比英语新华字典,根据目录定位词 ...

  5. 使用 MySQLi 和 PDO 向 MySQL 插入数据

    PHP MySQL 插入数据 使用 MySQLi 和 PDO 向 MySQL 插入数据 在创建完数据库和表后,我们可以向表中添加数据. 以下为一些语法规则: PHP 中 SQL 查询语句必须使用引号 ...

  6. PHP date_create_from_format() 函数

    ------------恢复内容开始------------ 实例 返回一个根据指定格式进行格式化的新的 DateTime 对象: <?php$date=date_create_from_for ...

  7. Python代码规范性检测

    一定要注重代码规范,按照平时的代码管理,可以将Python代码规范检测分为两种: 静态本地检测:可以借助静态检查工具,比如:Flake8,Pylint等,调研了一下,用Flake8的相对较多,功能满足 ...

  8. 利用Data Vault对数据仓库进行建模(二)

    写在前面 本篇先不讨论Data Vault其本身,因为不见得所有人都接受这个.但是里边有一些很不错的东西跟主流的数据仓库方法是有共同点的,所以这里主要讨论这些共同的方法,在笔者看来,无论是Kimbal ...

  9. Kaggle-pandas(1)

    Creating-reading-and-writing 戳我进原网站 教程 1.创建与导入 DataFrame import pandas as pd pd.DataFrame({'Yes': [5 ...

  10. 暑期java(面向对象设计)学习第一阶段总结

    0.前言 本次博客针对的是暑假学习java(面向对象设计)的前三次作业的小结,第一次作业:7-1 对三个整数排序 7-2 对四个整数排序 7-3 对十个整数进行排序 7-4 对多个整数进行排序 第二次 ...