余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

10年开发大牛带领程序员打造高性能系统缓存:Spring缓存案例分析

xiyangw 2023-10-08 13:53 35 浏览 0 评论

Spring缓存案例分析

介绍完Spring为缓存提供的核心组件,接下来通过一个完整的案例演示这些注解起到的效果,从而更好地为开发人员在日常开发

过程中引入Spring缓存机制来应对高并发场景提供基础。

在本案例中,我们将使用Redis作为我们的缓存媒介,而数据持久化使用的是MySQL数据库。

Spring所提供的Spring Data框架可以完成对这两种数据库的高效访问。

与使用Spring Data进行关系型数据库访问相似,使用SpringData Redis的第一步就是连接到Redis服务器。

想要实现连接,就需要获取RedisConnection,而获取RedisConnection的手段是使用RedisConnectionFactory接口。

Spring Data Redis对Redis操作做了封装,提供了一个工具类RedisTemplate,通过注入RedisConnectionFactory到RedisTemplate中,该RedisTemplate就能获取RedisConnection。

构建一个RedisTemplate的方法如代码清单6-17所示。

代码清单6-17 构建RedisTemplate示例代码

@Bean

public RedisTemplate<String, Integer>

redisTemplate(RedisConnectionFactory factory) {

RedisTemplate<String, Integer> redisTemplate = new RedisTemplate<>

();

redisTemplate.setKeySerializer(new StringRedisSerializer());

redisTemplate.setValueSerializer(new

JdkSerializationRedisSerializer());

redisTemplate.setExposeConnection(true);

redisTemplate.setConnectionFactory(factory);

redisTemplate.afterPropertiesSet();

return redisTemplate;

}

RedisTemplate为Redis交互提供了一个高级别的抽象。当RedisConnection提供低级别的方法来发送和返回二进制值时,RedisTemplate能够实现序列化和连接过程的自动化管理,从而将用户从这些细节中解放出来。

RedisTemplate提供了丰富的接口来操作Redis的特定数据类型,这些接口包括ValueOperations、ListOperations、SetOperations、ZSetOperations和HashOperations等,分别对应了Redis中String、List、Set、ZSet和Hash这五种常见的数据结构。

为了使用RedisTemplate,我们需要在pom文件中引入spring-boot-starter-data-redis组件。

本案例中完整的pom文件定义如代码清单6-18所示。

代码清单6-18 案例pom文件

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-cache</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-jpa</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>org.apache.commons</groupId>

<artifactId>commons-pool2</artifactId> </dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

</dependencies>

这里spring-boot-starter-cache、spring-boot-starter-data-jpa、spring-boot-starter-data-redis依赖包都非常重要。

我们接着来定义一个实体类User,如代码清单6-19所示。

代码清单6-19 User实体类定义代码

@Entity

@Table(name = "user_table")

@CacheConfig(cacheNames = "user")

public class User implements Serializable {

@Id

@GeneratedValue(strategy = GenerationType.IDENTITY)

@Column(name = "id")

protected Long id;

@Column(name = "user_name", nullable = false)

protected String userName;

@Temporal(TemporalType.TIMESTAMP)

@CreationTimestamp

@Column(name = "gmt_create", nullable = false, updatable = false)

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")

protected Date gmtCreate;

@Temporal(TemporalType.TIMESTAMP)

@UpdateTimestamp

@Column(name = "gmt_update", nullable = true, insertable = false)

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") protected Date gmtUpdate;

@Column(name = "valid", length = 1)

protected boolean valid = true;

}

上述User类中大量使用了Hibernate中的注解类规范字段的名称和长度等校验规则。同时,我们还注意到这里出现了一个@CacheConfig注解。如果我们在所有的@Cacheable注解中都需要设置value为user,那么就可以使用@CacheConfig来一次性完成对Cache名称的设置。

有了User对象,我们给出对应的数据访问层组件UserRepository,如代码清单6-20所示。

代码清单6-20 UserRepository接口定义代码

public interface UserRepository extends JpaRepository<User, Long>,

JpaSpecificationExecutor<User>, Serializable {

}

UserRepository接口定义非常简单,一方面直接扩展了Spring Data中的JpaRepository接口,另一方面也扩展了JpaSpecificationExecutor接口。

JpaSpecificationExecutor接口提供的是一种Specification查询机制。

考虑这样一种场景:我们需要查询某个实体,而给定的查询条件是不固定的,这时候就需要动态构建相应的查询语句。在Spring Data JPA中可以通过JpaSpecificationExecutor接口实现这类查询。相比于JPQL,使用Specification机制的优势是类型安全。例如,我们可以使用如代码清单6-21所示的实现方法来获取那些valid字段值为true的User对象。

代码清单6-21 Specification机制示例代码

Specification<User> specification = Specifications.

<User>and().eq("valid", true).build();

上述实现方法位于UserService中,完整的UserService实现如代码清单6-22所示。

代码清单6-22 UserService类代码

@Service

@EnableCaching

@CacheConfig(cacheNames = "user-object")

public class UserService {

@Autowired

protected UserRepository userRepository;

@CachePut(key = "#root.targetClass + '_' + #user.id")

public void save(User user) {

userRepository.save(user);

}

@CacheEvict(key = "#root.targetClass + '_' + #id")

public void delete(Long id) {

userRepository.deleteById(id);

}

@Cacheable(key = "#root.targetClass + '_' + #id")

public Optional<User> findOne(Long id) {

return userRepository.findById(id);

}

public List<User> findAll() {

Specification<User> specification = Specifications.

<User>and().eq("valid", true).build();

return userRepository.findAll(specification);

}

@Cacheable(value = "test", key = "#root.targetClass + '_' + #p0 +'_' + #p1")

public Page<User> findAll(int pageSize, int pageNumber) {

Specification<User> specification = Specifications.

<User>and().eq("valid", true).build();

Pageable pageable = PageRequest.of(pageNumber, pageSize);

return userRepository.findAll(specification, pageable);

}

}

可以看到,这里大量使用了Root对象来获取方法调用所涉及的一组元数据,并设置对应的key值。

有了UserService,就可以实现UserController并暴露一系列HTTP端点。

这里我们简单列举如代码清单6-23所示的分页方法,其他方法的实现也都类似。

代码清单6-23 UserController HTTP端点代码

@GetMapping(value = "/list")

public ResultModel getList(@RequestParam int pageSize, @RequestParam

int pageNumber) {

if (pageSize > 0) {

Page<User> page = userService.findAll(pageSize, pageNumber);

return ResultModel.ok(page);

} else {

List<User> entities = userService.findAll();

return ResultModel.ok(entities);

}

}

在案例的最后一部分,需要介绍的内容就是配置。我们在application.yml文件中添加如代码清单6-24所示的与缓存相关的配置项。

代码清单6-24 application.yml文件中的缓存配置信息

spring:

cache:

type: redis

redis:

time-to-live: 20000 #缓存超时时间,单位为ms

cache-null-values: false #是否缓存空值

redis:

port: 6379

host: localhost

lettuce:

pool:

max-active: 8

max-wait: -1

max-idle: 8

min-idle: 0

timeout: 10000 #redis连接超时时间,单位为ms

database: 0

现在,启动Spring Boot应用程序,并通过日志看缓存是否生效。在成功添加了几条User对象数据之后,执行一个根据id获取User对象的查询操作,可以得到如代码清单6-25所示的日志信息。

代码清单6-25 查询User时的日志信息

Hibernate: select user0_.id as id1_0_0_, user0_.gmt_create as

gmt_crea2_0_0_, user0_.gmt_update as gmt_upda3_0_0_, user0_.user_name

as user_nam4_0_0_, user0_.valid as valid5_0_0_ from user_table user0_

where user0_.id=?

再次执行该查询操作,我们发现控制台中不会再次出现该日志,说明针对查询操作的缓存已经生效。

其他针对User对象的操作,你可以参考案例代码并做一些尝试:https://github.com/tianminzheng/spring-bootexamples/tree/main/SpringCacheExample

本文给大家讲解的内容是springboot内置缓存:打造高性能系统缓存,Spring缓存案例分析

  • 下文给大家讲解的是springboot内置缓存:打造高性能系统缓存,缓存实现原理

相关推荐

“三次握手,四次挥手”你真的懂吗?

记得刚毕业找工作面试的时候,经常会被问到:你知道“3次握手,4次挥手”吗?这时候我会“胸有成竹”地“背诵”前期准备好的“答案”,第一次怎么怎么,第二次……答完就没有下文了,面试官貌似也没有深入下去的意...

面试官问:三次握手与四次挥手是怎么完成的?

作者|饶全成来源|码农桃花源记得刚毕业找工作面试的时候,经常会被问到:你知道“3次握手,4次挥手”吗?这时候我会“胸有成竹”地“背诵”前期准备好的“答案”,第一次怎么怎么,第二次……答完就没有...

三次握手和四次挥手的高阶面试题,建议收藏

昨天村长的讲解,真是一语点醒,这样的解释胜过死记硬背。但对于学习者,如果不能有直观感受,可能还是觉得不接地气,今天介绍两个工具,一个是网络抓包工具Wireshark,一个是linux命令tcpdum...

三次握手和四次挥手到底是个什么鬼东西

之前总有是有面试官喜欢问,你知道什么是三次握手么?什么是四次挥手么?为什么握手需要三次,挥手需要四次呢?今天我们就来详细的聊一下这个。1.什么是TCPTCP协议,简单称呼一下的话,那就是传输控制协议,...

加深理解TCP的三次握手与四次挥手

在了解三次握手和四次挥手之前,先要知道TCP报文内部包含了那些东西。熟悉了解TCP报文对日后学习网络和排除方面有很大的帮助,所以,今天为了加深对三次握手的理解,从新去认识TCP报文格式。TCP报文格式...

三次握手 与 四次挥手_三次握手四次挥手大白话

三次握手:①首先Client端发送连接请求报文②Server段接受连接后回复ACK报文,并为这次连接分配资源。③Client端接收到ACK报文后也向Server段发生ACK报文...

动画讲解TCP的3次握手,4次挥手,让你一次看明白

专注于Java领域优质技术,欢迎关注作者:老钱占小狼博客TCP三次握手和四次挥手的问题在面试中是最为常见的考点之一。很多读者都知道三次和四次,但是如果问深入一点,他们往往都无法作出准确回答。本篇尝试...

linux下实现免密传输文件或登录到其他服务器

使用scp传输文件到其他服务器的时候,提示需要输密码,如下:[root@18csetup]#scpLINUX.X64_180000_db_home.zip192.168.133.120:/u0...

Linux如何通过salt免密SCP传输上百台机的脚本?看chatGPT的回答

如何通过salt免密SCP传输上百台机的shell脚本”,下面是chatGPT给出的结果。scp批量免密脚本给出的详细shell脚本如下:#!/bin/bash#源文件路径和目标路径SRC_...

Linux/Mac scp命令上传文件_将hdfs上的文件下载到本地的命令是

语法scp[可选参数]file_sourcefile_target参数说明:-1:强制scp命令使用协议ssh1-2:强制scp命令使用协议ssh2-4:强制scp命令只使用IPv4寻...

Linux常用功能——文件远程传输_linux 远程传输文件

scp是securecopy的简写,是linux系统下基于ssh登陆进行安全的远程文件拷贝命令,用于在Linux下进行远程拷贝文件的命令。和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器...

使用 scp 命令定时拉取服务器备份文件

我们的服务器,每周五必须要做下备份,但总是忘记执行备份这件事情,或者是服务器备份做了,但没有做异地备份。所以通过定时任务自动备份,备份成功之后,在其它服务器上面通过定时任务scp命令自动拉取备份文...

windows下最轻便的FTP/SCP文件管理器

这次推荐的工具叫做winscp,这个工具如果是IT从业人员,又是做服务端相关工作的话,可能无人不知,如果是刚入门,推荐立马上手试试。如果看了觉得有用,欢迎收藏、点赞、关注。官方网站:https://w...

我不是网管 - Linux中使用SCP命令安全复制文件

SCP是linux发行版中的命令行工具,用于通过网络安全地跨系统复制文件和目录。SCP代表安全复制,因为它使用ssh协议复制文件。拷贝时,scp命令建立ssh连接到远程系统。换句话说...

WinSCP软件双系统(Win-Linux)文件传输教程

WinSCP软件是windows下的一款使用ssh协议的开源图形化SFTP客户端,也就是一个文件传输的软件,它有什么优点吗,咱们嵌入式开发中经常会将windows中的文件复制到linux系统当中,比较...

取消回复欢迎 发表评论: