5. SpringBoot缓存管理
5.1 默认缓存管理
Spring
框架支持透明地向应用程序添加缓存对缓存进行管理,其管理缓存的核心是将缓存应用于
操作数据的方法,从而减少操作数据的执行次数,同时不会对程序本身造成任何干扰。
Spring Boot
继承了
Spring
框架的缓存管理功能,通过使用
@EnableCaching
注解开启基于注解的缓存支持,Spring Boot
就可以启动缓存管理的自动化配置。
接下来针对
Spring Boot
支持的默认缓存管理进行讲解
5.1.1 基础环境搭建
1
.准备数据
使用创建的
springbootdata
的数据库,该数据库有两个表
t_article
和
t_comment
2
.创建项目
,
功能编写
(
1
)在
Dependencies
依赖选择项中添加
SQL
模块中的
JPA
依赖、
MySQL
依赖和
Web
模块中的
Web
依赖
(
2
)编写数据库表对应的实体类,并使用
JPA
相关注解配置映射关系
import javax . persistence . * ;@Entity ( name = "t_comment" ) // 设置 ORM 实体类,并指定映射的表名public class Comment {@Id // 表明映射对应的主键 id@GeneratedValue ( strategy = GenerationType . IDENTITY ) // 设置主键自增策略private Integer id ;private String content ;private String author ;@Column ( name = "a_id" ) // 指定映射的表字段名private Integer aId ;// 省略属性 getXX() 和 setXX() 方法// 省略 toString() 方法}
(
3
)编写数据库操作的
Repository
接口文件
public interface CommentRepository extends JpaRepository < Comment , Integer > {// 根据评论 id 修改评论作者 author@Transactional@Modifying@Query ( "update t_comment c set c.author = ?1 where c.id=?2" )public int updateComment ( String author , Integer id );}
(
4
)编写
service
层
@Servicepublic class CommentService {@Autowiredprivate CommentRepository commentRepository ;public Comment findCommentById ( Integer id ){Optional < Comment > comment = commentRepository . findById ( id );if ( comment . isPresent ()){Comment comment1 = comment . get ();return comment1 ;}return null ;}
(
5
)编写
Controller
层
@RestControllerpublic class CommentController {@Autowiredprivate CommentService commentService ;@RequestMapping ( value = "/findCommentById" )public Comment findCommentById ( Integer id ){Comment comment = commentService . findCommentById ( id );return comment ;}}
(
6
)编写配置文件
在项目全局配置文件
application.properties
中编写对应的数据库连接配置
# MySQL 数据库连接配置spring.datasource.url = jdbc : mysql : //localhost : 3306/springbootdata?serverTimezone = UTCspring.datasource.username = rootspring.datasource.password = root# 显示使用 JPA 进行数据库查询的 SQL 语句spring.jpa.show-sql = true# 开启驼峰命名匹配映射mybatis.configuration.map-underscore-to-camel-case = true# 解决乱码spring.http.encoding.force-response = true
(
7
)测试
![](https://i-blog.csdnimg.cn/blog_migrate/5a3cf910458dcf0f741799fe8ce99ab2.png)
上图情况,是因为没有在
Spring Boot
项目中开启缓存管理。在没有缓存管理的情况下,虽然数据表中的数据没有发生变化,但是每执行一次查询操作(本质是执行同样的SQL
语句),都会访问一次数据库并执行一次SQL
语句
5.1.2
默认缓存体验
在前面搭建的
Web
应用基础上,开启
Spring Boot
默认支持的缓存,体验
Spring Boot
默认缓存的使用效果
(
1
)使用
@EnableCaching
注解开启基于注解的缓存支持
@EnableCaching // 开启 Spring Boot 基于注解的缓存管理支持@SpringBootApplicationpublic class Springboot04CacheApplication {public static void main ( String [] args ) {SpringApplication . run ( Springboot04CacheApplication . class , args );}}
(
2
)使用
@Cacheable
注解对数据操作方法进行缓存管理。将
@Cacheable
注解标注在
Service
类的查询方法上,对查询结果进行缓存
/ 根据评论 id 查询评论信息@Cacheable ( cacheNames = "comment" )public Comment findById ( int comment_id ){Optional < Comment > optional = commentRepository . findCommentById ( comment_id );if ( optional . isPresent ()){return optional . get ();}return null ;}
上述代码中,在
CommentService
类中的
fifindCommentById(int comment_id)
方法上添加了查询缓存注解@Cacheable
,该注解的作用是将查询结果
Comment
存放在
Spring Boot
默认缓存中名称为
comment的名称空间(namespace
)中,对应缓存唯一标识
(即缓存数据对应的主键
k
)默认为方法参数
comment_id
的值
(
3
)测试访问
![](https://i-blog.csdnimg.cn/blog_migrate/ff4c595de70e8d0f3883962efe41c539.png)
可以看出,再次 执行
fifindCommentById()
方法正确查询出用户评论信息
Comment
,在配置了
SpringBoot默认注解后,重复进行同样的查询操作,数据库只执行了一次
SQL
查询语句,说明项目开启的默认缓存支持已经生效
- 底层结构:在诸多的缓存自动配置类中, SpringBoot默认装配的是 SimpleCacheConfiguration , 他使用的 CacheManager 是 ConcurrentMapCacheManager, 使用 ConcurrentMap 当底层的数据结构,按照Cache的名字查询出Cache, 每一个Cache中存在多个k-v键值对,缓存值
(
4
)缓存注解介绍
刚刚通过使用
@EnableCaching
、
@Cacheable
注解实现了
Spring Boot
默认的基于注解的缓存管
理,除此之外,还有更多的缓存注解及注解属性可以配置优化缓存管理
1
.
@EnableCaching
注解
@EnableCaching
是由
spring
框架提供的,
springboot
框架对该注解进行了继承,该注解需要配置在类上(在中,通常配置在项目启动类上),用于开启基于注解的缓存支持
2
.
@Cacheable
注解
@Cacheable
注解也是由
spring
框架提供的,可以作用于类或方法(通常用在数据查询方法上),用于对方法结果进行缓存存储。注解的执行顺序是,先进行缓存查询,如果为空则进行方法查询,并将结果进行缓存;如果缓存中有数据,不进行方法查询,而是直接使用缓存数据
@Cacheable
注解提供了多个属性,用于对缓存存储进行相关配置
![](https://i-blog.csdnimg.cn/blog_migrate/0b7c6723911d06c788ba317f5f5dd639.png)
执行流程
&
时机
方法运行之前,先去查询
Cache
(缓存组件),按照
cacheNames
指定的名字获取,(
CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache
组件会自动创建;
去
Cache
中查找缓存的内容,使用一个
key
,默认就是方法的参数,如果多个参数或者没有参数,是按照某种策略生成的,默认是使用KeyGenerator
生成的,使用
SimpleKeyGenerator
生成
key
,
SimpleKeyGenerator
生成
key
的默认策略:
![](https://i-blog.csdnimg.cn/blog_migrate/6da91d5cd5118bf3aae0e45341c29412.png)
常用的
SPEL
表达式
![](https://i-blog.csdnimg.cn/blog_migrate/ca4d2f8fd832d8196ccddf3e77d9c011.png)
3
.
@CachePut
注解
目标方法执行完之后生效
, @CachePut
被使用于修改操作比较多
,
哪怕缓存中已经存在目标值了
,
但是这个注解保证这个方法依然会执行
,
执行之后的结果被保存在缓存中
@CachePut
注解也提供了多个属性,这些属性与
@Cacheable
注解的属性完全相同。
更新操作
,
前端会把
id+
实体传递到后端使用
,
我们就直接指定方法的返回值从新存进缓存时的
key="#id"
,
如果前端只是给了实体
,
我们就使用
key="#
实体
.id"
获取
key.
同时
,
他的执行时机是目标
方法结束后执行
,
所以也可以使用
key="#result.id"
,
拿出返回值的
id
4
.
@CacheEvict
注解
@CacheEvict
注解是由
Spring
框架提供的,可以作用于类或方法(通常用在数据删除方法上),该注解的作用是删除缓存数据。@CacheEvict
注解的默认执行顺序是,先进行方法调用,然后将缓存进行清除。
5.2 整合Redis缓存实现
5.2.1 Spring Boot支持的缓存组件
在
Spring Boot
中,数据的缓存管理存储依赖于
Spring
框架中
cache
相关的
org.springframework.cache.Cache
和
org.springframework.cache.CacheManager
缓存管理器接口。
如果程序中没有定义类型为
CacheManager
的
Bean
组件或者是名为
cacheResolver
的
CacheResolver
缓存解析器,Spring Boot
将尝试选择并启用以下缓存组件(按照指定的顺序):
(
1
)
Generic
(
2
)
JCache (JSR-107) (EhCache 3
、
Hazelcast
、
Infifinispan
等
)
(
3
)
EhCache 2.x
(
4
)
Hazelcast
(
5
)
Infifinispan
(
6
)
Couchbase
(
7
)
Redis
(
8
)
Caffffeine
(
9
)
Simple
上面按照
Spring Boot
缓存组件的加载顺序,列举了支持的
9
种缓存组件,在项目中添加某个缓存管理组件(例如Redis
)后,
Spring Boot
项目会选择并启用对应的缓存管理器。如果项目中同时添加了多个缓存组件,且没有指定缓存管理器或者缓存解析器(CacheManager
或者
cacheResolver
),那么Spring Boot会按照上述顺序在添加的多个缓存中优先启用指定的缓存组件进行缓存管理。
刚刚讲解的
Spring Boot
默认缓存管理中,没有添加任何缓存管理组件能实现缓存管理。这是因为开启缓存管理后,Spring Boot
会按照上述列表顺序查找有效的缓存组件进行缓存管理,如果没有任何缓存组件,会默认使用最后一个Simple
缓存组件进行管理。
Simple
缓存组件是
Spring Boot
默认的缓存管理组件,它默认使用内存中的ConcurrentMap
进行缓存存储,所以在没有添加任何第三方缓存组件的情况下,可以实现内存中的缓存管理,但是我们不推荐使用这种缓存管理方式
5.2.2 基于注解的Redis缓存实现
在
Spring Boot
默认缓存管理的基础上引入
Redis
缓存组件,使用基于注解的方式讲解
Spring Boot
整合
Redis
缓存的具体实现
(
1
)添加
Spring Data Redis
依赖启动器。在
pom.xml
文件中添加
Spring Data Redis
依赖启动器
<dependency><groupId> org.springframework.boot </groupId><artifactId> spring-boot-starter-data-redis </artifactId></dependency>
当我们添加进
redis
相关的启动器之后
, SpringBoot
会使用
RedisCacheConfigratioin
当做生效的自动配置类进行缓存相关的自动装配,
容器中使用的缓存管理器是
RedisCacheManager
,
这个缓存管理器创建的
Cache
为
RedisCache
,
进而操控
redis
进行数据的缓存
(
2
)
Redis
服务连接配置
# Redis 服务地址spring.redis.host = 127.0.0.1# Redis 服务器连接端口spring.redis.port = 6379# Redis 服务器连接密码(默认为空)spring.redis.password =
(
3
)对
CommentService
类中的方法进行修改使用
@Cacheable
、
@CachePut
、
@CacheEvict
三个注解定制缓存管理,分别进行缓存存储、缓存更新和缓存删除的演示
@Servicepublic class CommentService {@Autowiredprivate CommentRepository commentRepository ;@Cacheable ( cacheNames = "comment" , unless = "#result==null" )public Comment findCommentById ( Integer id ){Optional < Comment > comment = commentRepository . findById ( id );if ( comment . isPresent ()){Comment comment1 = comment . get ();return comment1 ;}return null ;}@CachePut ( cacheNames = "comment" , key = "#result.id" )public Comment updateComment ( Comment comment ) {commentRepository . updateComment ( comment . getAuthor (), comment . getaId ());return comment ;}@CacheEvict ( cacheNames = "comment" )public void deleteComment ( int comment_id ) {commentRepository . deleteById ( comment_id );}}
以上 使用
@Cacheable
、
@CachePut
、
@CacheEvict
注解在数据查询、更新和删除方法上进行了缓存管理。
其中,查询缓存
@Cacheable
注解中没有标记
key
值,将会使用默认参数值
comment_id
作为
key
进行数据保存,在进行缓存更新时必须使用同样的key
;同时在查询缓存
@Cacheable
注解中,定义了 “unless = "#result==null"”表示查询结果为空不进行缓存
(
4
)
基于注解的
Redis
查询缓存测试
![](https://i-blog.csdnimg.cn/blog_migrate/80f1478e3283d02b3e02b38c81c69d0f.png)
可以看出,查询用户评论信息
Comment
时执行了相应的
SQL
语句,但是在进行缓存存储时出现了
IllegalArgumentException
非法参数异常,提示信息要求对应
Comment
实体类必须实现序列化
(
“DefaultSerializer requires a Serializable payload but received an object of type”
)。
(
5
)将缓存对象实现序列化。
![](https://i-blog.csdnimg.cn/blog_migrate/285352306c2b211c4fc5a5a579530da6.png)
(
6
)再次启动测试
访问
“
http://localhost:8080/fifindCommentById?id=1
”
查询
id
为
1
的用户评论信息,并重复刷新浏
览器查询同一条数据信息,查询结果
![](https://i-blog.csdnimg.cn/blog_migrate/c222d785d939de75cca62ef8fe22cef2.png)
查看控制台打印的
SQL
查询语句
![](https://i-blog.csdnimg.cn/blog_migrate/027c1dd3f4c9cdc1a284779f6250700a.png)
还可以打开
Redis
客户端可视化管理工具
Redis Desktop Manager
连接本地启用的
Redis
服务,查看具体的数据缓存效果
![](https://i-blog.csdnimg.cn/blog_migrate/9b02f75d1615fb36f32b7d812c2cfefe.png)
执行
fifindById()
方法查询出的用户评论信息
Comment
正确存储到了
Redis
缓存库中名为
comment
的
名称空间下。其中缓存数据的唯一标识
key
值是以
“
名称空间
comment::+
参数值(
comment::1
)
”
的字符串形式体现的,而value
值则是经过
JDK
默认序列格式化后的
HEX
格式存储。这种
JDK
默认序列格式化后的数据显然不方便缓存数据的可视化查看和管理,所以在实际开发中,通常会自定义数据的序列化格式
(
7
) 基于注解的Redis
缓存更新测试。
先通过浏览器访问
“
http://localhost:8080/update/1/shitou
”
更新
id
为
1
的评论作者名为
shitou
;
接着,继续访问
“
http://localhost:8080/get/1
”
查询
id
为
1
的用户评论信息
![](https://i-blog.csdnimg.cn/blog_migrate/a83ad369f9d606f95fe6ca01e1702f0d.png)
可以看出,执行
updateComment()
方法更新
id
为
1
的数据时执行了一条更新
SQL
语句,后续调用
fifindById()
方法查询
id
为
1
的用户评论信息时没有执行查询
SQL
语句,且浏览器正确返回了更新后的结果,表明@CachePut
缓存更新配置成功
(
8
)基于注解的
Redis
缓存删除测试
通过浏览器访问
“
http://localhost:8080/deleteComment?id=1
”
删除
id
为
1
的用户评论信息;
![](https://i-blog.csdnimg.cn/blog_migrate/3793aba7088190a2c285f631dac85202.png)
执行
deleteComment()
方法删除
id
为
1
的数据后查询结果为空,之前存储在
Redis
数据库的
comment
相关数据也被删除,表明
@CacheEvict
缓存删除成功实现
通过上面的案例可以看出,使用基于注解的
Redis
缓存实现只需要添加
Redis
依赖并使用几个注解
可以实现对数据的缓存管理。另外,还可以在
Spring Boot
全局配置文件中配置
Redis
有效期,示例代码如下:
# 对基于注解的 Redis 缓存数据统一设置有效期为 1 分钟,单位毫秒spring.cache.redis.time-to-live = 60000
上述代码中,在
Spring Boot
全局配置文件中添加了
“spring.cache.redis.time-to-live”
属性统一配
置
Redis
数据的有效期(单位为毫秒),但这种方式相对来说不够灵活
5.2.3 基于API的Redis缓存实现
在
Spring Boot
整合
Redis
缓存实现中,除了基于注解形式的
Redis
缓存实现外,还有一种开发中常用的方式——
基于
API
的
Redis
缓存实现。这种基于
API
的
Redis
缓存实现,需要在某种业务需求下通过Redis提供的
API
调用相关方法实现数据缓存管理;同时,这种方法还可以手动管理缓存的有效期。
下面,通过
Redis API
的方式讲解
Spring Boot
整合
Redis
缓存的具体实现
(
1
)使用
Redis API
进行业务数据缓存管理。在
com.lagou.service
包下编写一个进行业务处理的
类
ApiCommentService
@Servicepublic class ApiCommentService {@Autowiredprivate CommentRepository commentRepository ;@Autowiredprivate RedisTemplate redisTemplate ;public Comment findCommentById ( Integer id ){Object o = redisTemplate . opsForValue (). get ( "comment_" + id );if ( o != null ){return ( Comment ) o ;} else {// 缓存中没有,从数据库查询Optional < Comment > byId = commentRepository . findById ( id );if ( byId . isPresent ()){Comment comment = byId . get ();// 将查询结果存入到缓存中,并设置有效期为 1 天redisTemplate . opsForValue (). set ( "comment_" + id , comment , 1 , TimeUnit . DAYS );return comment ;} else {return null ;}}}public Comment updateComment ( Comment comment ) {commentRepository . updateComment ( comment . getAuthor (), comment . getaId ());// 更新数据后进行缓存更新redisTemplate . opsForValue (). set ( "comment_" + comment . getId (), comment );return comment ;}public void deleteComment ( int comment_id ) {commentRepository . deleteById ( comment_id );redisTemplate . delete ( "comment_" + comment_id );}
(
2
)编写
Web
访问层
Controller
文件
@RestController@RequestMapping ( "api" ) // 窄化请求路径public class ApiCommentController {@Autowiredprivate ApiCommentService commentService ;@RequestMapping ( value = "/findCommentById" )public Comment findCommentById ( Integer id ){Comment comment = commentService . findCommentById ( id );return comment ;}@RequestMapping ( value = "/updateComment" )public void updateComment ( Comment comment ){Comment comment2 = commentService . findCommentById ( comment . getId ());comment . setAuthor ( comment . getAuthor ());commentService . updateComment ( comment );}@RequestMapping ( value = "/deleteComment" )public void deleteComment ( int id ){commentService . deleteComment ( id );}}
- 基于注解的缓存支持,所以这里可以选择将添加在项目启动类上的@EnableCaching进行删除或者注释
5.3 自定义Redis缓存序列化机制
刚刚完成了
Spring Boot
整合
Redis
进行了数据的缓存管理,但缓存管理的实体类数据使用的是
JDK
序列化方式,不便于使用可视化管理工具进行查看和管理。
![](https://i-blog.csdnimg.cn/blog_migrate/600e3e16c48bde2a132edc58a76df6e2.png)
接下来分别针对基于注解的
Redis
缓存实现和基于
API
的
Redis
缓存实现中的数据序列化机制进行介绍,并自定义JSON
格式的数据序列化方式进行数据缓存管理
5.3.1 自定义RedisTemplate
1
.
Redis API
默认序列化机制
基于
API
的
Redis
缓存实现是使用
RedisTemplate
模板进行数据缓存操作的,这里打开
RedisTemplate
类,查看该类的源码信息
public class RedisTemplate < K , V > extends RedisAccessorimplements RedisOperations < K , V > ,BeanClassLoaderAware {// 声明了 key 、 value 的各种序列化方式,初始值为空@Nullableprivate RedisSerializer keySerializer = null ;@Nullableprivate RedisSerializer valueSerializer = null ;@Nullableprivate RedisSerializer hashKeySerializer = null ;@Nullableprivate RedisSerializer hashValueSerializer = null ;...// 进行默认序列化方式设置,设置为 JDK 序列化方式public void afterPropertiesSet () {super . afterPropertiesSet ();boolean defaultUsed = false ;if ( this . defaultSerializer == null ) {this . defaultSerializer = new JdkSerializationRedisSerializer (this . classLoader != null ?this . classLoader : this . getClass (). getClassLoader ());}...}...}
从上述
RedisTemplate
核心源码可以看出,在
RedisTemplate
内部声明了缓存数据
key
、
value
的各
种序列化方式,且初始值都为空;在
afterPropertiesSet()
方法中,判断如果默认序列化参数
defaultSerializer
为空,将数据的默认序列化方式设置为
JdkSerializationRedisSerializer
根据上述源码信息的分析,可以得到以下两个重要的结论:
(
1
)使用
RedisTemplate
进行
Redis
数据缓存操作时,内部默认使用的是
JdkSerializationRedisSerializer
序列化方式,所以进行数据缓存的实体类必须实现
JDK
自带的序列化接口(例如Serializable
);
(
2
)使用
RedisTemplate
进行
Redis
数据缓存操作时,如果自定义了缓存序列化方式
defaultSerializer
,那么将使用自定义的序列化方式。
另外,在
RedisTemplate
类源码中,看到的缓存数据
key
、
value
的各种序列化类型都是
RedisSerializer
。进入
RedisSerializer
源码查看
RedisSerializer
支持的序列化方式(进入该类后,使用Ctrl+Alt+左键单击类名查看)
image-20200427185943034
可以看出,
RedisSerializer
是一个
Redis
序列化接口,默认有
6
个实现类,这
6
个实现类代表了
6
种
不同的数据序列化方式。其中,
JdkSerializationRedisSerializer
是
JDK
自带的,也是
RedisTemplate
内部默认使用的数据序列化方式,开发者可以根据需要选择其他支持的序列化方式(例如JSON
方式)
2
.自定义
RedisTemplate
序列化机制
在项目中引入
Redis
依赖后,
Spring Boot
提供的
RedisAutoConfifiguration
自动配置会生效。打开
RedisAutoConfifiguration
类,查看内部源码中关于
RedisTemplate
的定义方式
public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBean (name = { "redisTemplate" })public RedisTemplate < Object , Object > redisTemplate ( RedisConnectionFactoryredisConnectionFactory ) throwsUnknownHostException {RedisTemplate < Object , Object > template = new RedisTemplate ();template . setConnectionFactory ( redisConnectionFactory );return template ;}...}
从上述
RedisAutoConfifiguration
核心源码中可以看出,在
Redis
自动配置类中,通过
Redis
连接工厂RedisConnectionFactory初始化了一个
RedisTemplate
;该类上方添加了
@ConditionalOnMissingBean
注解(顾名思义,当某个
Bean
不存在时生效),用来表明如果开发者自定义了一个名为redisTemplate
的
Bean
,则该默认初始化的
RedisTemplate
不会生效。
如果想要使用自定义序列化方式的
RedisTemplate
进行数据缓存操作,可以参考上述核心代码创建
一个名为
redisTemplate
的
Bean
组件,并在该组件中设置对应的序列化方式即可
接下来,在项目中创建名为
com.lagou.confifig
的包,在该包下创建一个
Redis
自定义配置类
RedisConfifig
,并按照上述思路自定义名为
redisTemplate
的
Bean
组件
@Configurationpublic class RedisConfig {@Beanpublic RedisTemplate < Object , Object > redisTemplate ( RedisConnectionFactoryredisConnectionFactory ) {RedisTemplate < Object , Object > template = new RedisTemplate ();template . setConnectionFactory ( redisConnectionFactory );// 使用 JSON 格式序列化对象,对缓存数据 key 和 value 进行转换Jackson2JsonRedisSerializer jacksonSeial = newJackson2JsonRedisSerializer ( Object . class );// 解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper ();om . setVisibility ( PropertyAccessor . ALL , JsonAutoDetect . Visibility . ANY );om . enableDefaultTyping ( ObjectMapper . DefaultTyping . NON_FINAL );jacksonSeial . setObjectMapper ( om );// 设置 RedisTemplate 模板 API 的序列化方式为 JSONtemplate . setDefaultSerializer ( jacksonSeial );return template ;}}
通过
@Confifiguration
注解定义了一个
RedisConfifig
配置类,并使用
@Bean
注解注入了一个默认名
称为方法名的
redisTemplate
组件(注意,该
Bean
组件名称必须是
redisTemplate
)。在定义的
Bean
组件中,自定义了一个RedisTemplate
,使用自定义的
Jackson2JsonRedisSerializer
数据序列化方式;在定制序列化方式中,定义了一个ObjectMapper
用于进行数据转换设置
3
.效果测试
启动项目,项目启动成功后,通过浏览器访问“
http://localhost:8080/api/fifindCommentById?id=
3
”
查询
id
为
3
的用户评论信息,并重复刷新浏览器查看同一条数据信息
![](https://i-blog.csdnimg.cn/blog_migrate/750cc26699dc920ce18ab78f59784c4d.png)
查看控制台打印的
SQL
查询语句
![](https://i-blog.csdnimg.cn/blog_migrate/cfa700d181f5f1cde1f7bb3cea526ec1.png)
可以看出,执行
fifindById()
方法正确查询出用户评论信息
Comment
,重复进行同样的查询操作,数据库只执行了一次SQL
语句,这说明定制的
Redis
缓存生效。
使用
Redis
客户端可视化管理工具
Redis Desktop Manager
查看缓存数据 :
![](https://i-blog.csdnimg.cn/blog_migrate/e1fbedbb06d5db332b90ca35d269e8f0.png)
执行
fifindById()
方法查询出用户评论信息
Comment
正确存储到了
Redis
缓存库中,且缓存到
Redis
服务的数据已经使用了
JSON
格式存储展示,查看和管理也非常方便,说明自定义的
Redis API
模板工具RedisTemplate生效
5.3.2 自定义RedisCacheManager
刚刚针对基于
API
方式的
RedisTemplate
进行了自定义序列化方式的改进,从而实现了
JSON
序列化方式缓存数据,但是这种自定义的RedisTemplate
对于基于注解的
Redis
缓存来说,是没有作用的。接下来,针对基于注解的Redis
缓存机制和自定义序列化方式进行讲解
1
.
Redis
注解默认序列化机制
打开
Spring Boot
整合
Redis
组件提供的缓存自动配置类
RedisCacheConfifiguration
(
org.springframework.boot.autoconfifigure.cache
包下的),查看该类的
源码信息,其核心代码如下
@Configurationclass RedisCacheConfiguration {@Beanpublic RedisCacheManager cacheManager ( RedisConnectionFactoryredisConnectionFactory , ResourceLoaderresourceLoader ) {RedisCacheManagerBuilder builder =RedisCacheManager . builder ( redisConnectionFactory ). cacheDefaults ( this . determineConfiguration ( resourceLoader . getClassLoader ()));List < String > cacheNames = this . cacheProperties . getCacheNames ();if ( ! cacheNames . isEmpty ()) {builder . initialCacheNames ( new LinkedHashSet ( cacheNames ));}return( RedisCacheManager ) this . customizerInvoker . customize ( builder . build ());}private org . springframework . data . redis . cache . RedisCacheConfigurationdetermineConfiguration ( ClassLoader classLoader ){if ( this . redisCacheConfiguration != null ) {return this . redisCacheConfiguration ;} else {Redis redisProperties = this . cacheProperties . getRedis ();org . springframework . data . redis . cache . RedisCacheConfiguration config=org . springframework . data . redis . cache . RedisCacheConfiguration . defaultCacheConfig();config =config . serializeValuesWith ( SerializationPair . fromSerializer (newJdkSerializationRedisSerializer ( classLoader )));...return config ;}}}
从上述核心源码中可以看出,同
RedisTemplate
核心源码类似,
RedisCacheConfifiguration
内部同样通过Redis
连接工厂
RedisConnectionFactory
定义了一个缓存管理器
RedisCacheManager
;同时定制RedisCacheManager时,也默认使用了
JdkSerializationRedisSerializer
序列化方式。
如果想要使用自定义序列化方式的
RedisCacheManager
进行数据缓存操作,可以参考上述核心代
码创建一个名为
cacheManager
的
Bean
组件,并在该组件中设置对应的序列化方式即可
- 注意,在Spring Boot 2.X版本中,RedisCacheManager是单独进行构建的。因此,在Spring Boot 2.X版本中,对RedisTemplate进行自定义序列化机制构建后,仍然无法对 RedisCacheManager内部默认序列化机制进行覆盖(这也就解释了基 于注解的Redis缓存实现仍然会使用JDK默认序列化机制的原因),想要基于注解的Redis缓存实现也使用自定义序列化机制,需要自定义RedisCacheManager
2
.自定义
RedisCacheManager
在项目的
Redis
配置类
RedisConfifig
中,按照上一步分析的定制方法自定义名为
cacheManager
的
Bean
组件
@Beanpublic RedisCacheManager cacheManager ( RedisConnectionFactoryredisConnectionFactory ) {// 分别创建 String 和 JSON 格式序列化对象,对缓存数据 key 和 value 进行转换RedisSerializer < String > strSerializer = new StringRedisSerializer ();Jackson2JsonRedisSerializer jacksonSeial =new Jackson2JsonRedisSerializer ( Object . class );// 解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper ();om . setVisibility ( PropertyAccessor . ALL , JsonAutoDetect . Visibility . ANY );om . enableDefaultTyping ( ObjectMapper . DefaultTyping . NON_FINAL );jacksonSeial . setObjectMapper ( om );// 定制缓存数据序列化方式及时效RedisCacheConfiguration config =RedisCacheConfiguration . defaultCacheConfig (). entryTtl ( Duration . ofDays ( 1 )). serializeKeysWith ( RedisSerializationContext . SerializationPair. fromSerializer ( strSerializer )). serializeValuesWith ( RedisSerializationContext . SerializationPair. fromSerializer ( jacksonSeial )). disableCachingNullValues ();RedisCacheManager cacheManager = RedisCacheManager. builder ( redisConnectionFactory ). cacheDefaults ( config ). build ();return cacheManager ;}
上述代码中,在
RedisConfifig
配置类中使用
@Bean
注解注入了一个默认名称为方法名的
cacheManager
组件。在定义的
Bean
组件中,通过
RedisCacheConfifiguration
对缓存数据的
key
和
value 分别进行了序列化方式的定制,其中缓存数据的key
定制为
StringRedisSerializer
(即
String
格式),而value定制为了
Jackson2JsonRedisSerializer
(即
JSON
格式),同时还使用
entryTtl(Duration.ofDays(1))
方法将缓存数据有效期设置为
1
天
完成基于注解的
Redis
缓存管理器
RedisCacheManager
定制后,可以对该缓存管理器的效果进行
测试(使用自定义序列化机制的
RedisCacheManager
测试时,实体类可以不用实现序列化接口)