当前位置: 首页 > news >正文

【Java项目脚手架系列】第七篇:Spring Boot + Redis项目脚手架

【Java项目脚手架系列】第七篇:Spring Boot + Redis项目脚手架

前言

在前面的文章中,我们介绍了 Spring Boot + JPA 项目脚手架。今天,我们将介绍 Spring Boot + Redis 项目脚手架,这是一个用于快速搭建缓存应用的框架。

什么是 Spring Boot + Redis?

Spring Boot + Redis 是一个强大的组合,它提供了:

  1. Spring Boot 的快速开发能力
  2. Redis 的高性能缓存支持
  3. 完整的缓存操作支持
  4. 连接池管理能力
  5. 测试框架支持

技术栈

  • Spring Boot 2.7.0:核心框架
  • Spring Data Redis:缓存框架
  • Redis 6.2:缓存数据库
  • JUnit 5:测试框架
  • Mockito:测试框架
  • Maven 3.9.6:项目构建工具

Spring Boot + Redis 项目脚手架

1. 项目结构

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           ├── RedisApplication.java        # 应用程序入口
│   │           ├── config
│   │           │   └── RedisConfig.java        # Redis 配置类(使用 StringRedisTemplate)
│   │           ├── controller
│   │           │   └── RedisController.java    # Redis 操作控制器
│   │           └── service
│   │               └── RedisService.java       # Redis 服务类(基于 StringRedisTemplate)
│   └── resources
│       └── application.yml                     # 主配置文件
└── test├── java│   └── com│       └── example│           ├── controller│           │   └── RedisControllerTest.java    # 控制器测试类│           └── service│               └── RedisServiceTest.java       # 服务层测试类└── resources└── application-test.yml                   # 测试环境配置文件

2. 核心文件内容

2.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>springboot-redis-scaffold</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.0</version><relativePath/></parent><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding></properties><dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- Test --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Redis connection pool dependency --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>
2.2 application.yml
server:port: 8080spring:redis:host: localhostport: 6379database: 0timeout: 10000lettuce:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0logging:level:com.example: debugorg.springframework.data.redis: debug
2.3 application-test.yml
spring:redis:host: localhostport: 6379database: 1  # 使用不同的数据库进行测试timeout: 1000lettuce:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0

3. 核心功能实现

3.1 Redis配置类
@Configuration
public class RedisConfig {@Beanpublic RedisConnectionFactory redisConnectionFactory() {RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();redisConfig.setHostName("localhost");redisConfig.setPort(6379);redisConfig.setDatabase(0);LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder().commandTimeout(Duration.ofSeconds(5)).clientOptions(ClientOptions.builder().socketOptions(SocketOptions.builder().connectTimeout(Duration.ofSeconds(5)).build()).build()).build();return new LettuceConnectionFactory(redisConfig, clientConfig);}@Beanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {return new StringRedisTemplate(connectionFactory);}
}
3.2 Redis服务类
@Service
@RequiredArgsConstructor
public class RedisService {private final StringRedisTemplate stringRedisTemplate;/*** 设置缓存*/public void set(String key, String value) {stringRedisTemplate.opsForValue().set(key, value);}/*** 设置缓存并设置过期时间*/public void set(String key, String value, long timeout, TimeUnit unit) {stringRedisTemplate.opsForValue().set(key, value, timeout, unit);}/*** 获取缓存*/public String get(String key) {return stringRedisTemplate.opsForValue().get(key);}/*** 删除缓存*/public Boolean delete(String key) {return stringRedisTemplate.delete(key);}/*** 判断key是否存在*/public Boolean exists(String key) {return stringRedisTemplate.hasKey(key);}/*** 设置过期时间*/public Boolean expire(String key, long timeout, TimeUnit unit) {return stringRedisTemplate.expire(key, timeout, unit);}
}
3.3 Redis控制器
@RestController
@RequestMapping("/api/redis")
@RequiredArgsConstructor
public class RedisController {private final RedisService redisService;private final RedisConnectionFactory redisConnectionFactory;@GetMapping("/health")public String health() {try {redisConnectionFactory.getConnection().ping();return "OK";} catch (Exception e) {return "Redis connection error: " + e.getMessage();}}@PostMapping("/{key}")public void setValue(@PathVariable String key, @RequestBody String value) {redisService.set(key, value);}@PostMapping("/{key}/expire/{timeout}")public void setValueWithExpire(@PathVariable String key, @RequestBody String value,@PathVariable long timeout) {redisService.set(key, value, timeout, TimeUnit.SECONDS);}@GetMapping("/{key}")public String getValue(@PathVariable String key) {return redisService.get(key);}@DeleteMapping("/{key}")public Boolean deleteValue(@PathVariable String key) {return redisService.delete(key);}@GetMapping("/{key}/exists")public Boolean hasKey(@PathVariable String key) {return redisService.exists(key);}
}

4. 测试实现

4.1 服务层测试
@SpringBootTest
@ActiveProfiles("test")
public class RedisServiceTest {@Autowiredprivate RedisService redisService;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@BeforeEachvoid setUp() {// 每个测试前清理所有键stringRedisTemplate.getConnectionFactory().getConnection().flushAll();}@Testvoid testSetAndGet() {// GivenString key = "test:key";String value = "test:value";// WhenredisService.set(key, value);String result = redisService.get(key);// ThenassertEquals(value, result);}@Testvoid testDelete() {// GivenString key = "test:key";String value = "test:value";redisService.set(key, value);// WhenredisService.delete(key);String result = redisService.get(key);// ThenassertNull(result);}@Testvoid testSetWithExpiration() throws InterruptedException {// GivenString key = "test:expire:key";String value = "test:expire:value";long timeout = 1; // 1 second// WhenredisService.set(key, value, timeout, TimeUnit.SECONDS);String result = redisService.get(key);// ThenassertEquals(value, result);// Wait for expirationThread.sleep(1100);String expiredResult = redisService.get(key);assertNull(expiredResult);}@Testvoid testExists() {// GivenString key = "test:exists:key";String value = "test:exists:value";// WhenredisService.set(key, value);boolean exists = redisService.exists(key);boolean notExists = redisService.exists("non:existing:key");// ThenassertTrue(exists);assertFalse(notExists);}
}
4.2 控制器层测试
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
public class RedisControllerTest {@Autowiredprivate MockMvc mockMvc;@Autowiredprivate RedisService redisService;@BeforeEachvoid setUp() {// 每个测试前清理测试键redisService.delete("test:key");}@Testvoid testSetValue() throws Exception {mockMvc.perform(post("/api/redis/test:key").contentType(MediaType.TEXT_PLAIN).content("test:value")).andExpect(status().isOk());}@Testvoid testGetValue() throws Exception {// GivenredisService.set("test:key", "test:value");// When/ThenmockMvc.perform(get("/api/redis/test:key")).andExpect(status().isOk()).andExpect(content().string("test:value"));}@Testvoid testDeleteValue() throws Exception {// GivenredisService.set("test:key", "test:value");// When/ThenmockMvc.perform(delete("/api/redis/test:key")).andExpect(status().isOk());// Verify deletionmockMvc.perform(get("/api/redis/test:key")).andExpect(status().isNotFound());}@Testvoid testSetValueWithExpiration() throws Exception {mockMvc.perform(post("/api/redis/test:key/expire/1").contentType(MediaType.TEXT_PLAIN).content("test:value")).andExpect(status().isOk());}@Testvoid testCheckKeyExists() throws Exception {// GivenredisService.set("test:key", "test:value");// When/ThenmockMvc.perform(get("/api/redis/test:key/exists")).andExpect(status().isOk()).andExpect(content().string("true"));mockMvc.perform(get("/api/redis/non:existing:key/exists")).andExpect(status().isOk()).andExpect(content().string("false"));}@Testvoid testHealthCheck() throws Exception {mockMvc.perform(get("/api/redis/health")).andExpect(status().isOk()).andExpect(content().string("OK"));}
}

5. 使用说明

  1. 确保已安装并启动 Redis 服务器
  2. 修改 application.yml 中的 Redis 配置(如果需要)
  3. 运行 RedisApplication 类启动应用
  4. 访问 API 接口:
    • POST /api/redis/{key} - 设置字符串值
    • POST /api/redis/{key}/expire/{timeout} - 设置带过期时间的字符串值
    • GET /api/redis/{key} - 获取字符串值
    • DELETE /api/redis/{key} - 删除值
    • GET /api/redis/{key}/exists - 检查键是否存在
    • GET /api/redis/health - 健康检查

6. 注意事项

  1. 确保 Redis 服务器已启动
  2. 默认使用本地 Redis 服务器(localhost:6379)
  3. 所有操作都使用 String 类型,确保类型安全
  4. 测试使用独立的数据库,不会影响生产数据

7. 遇到的问题

问题一:连接池依赖缺失

在项目启动时,遇到了以下错误:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisConnectionFactory' defined in class path resource [com/example/config/RedisConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.redis.connection.RedisConnectionFactory]: Factory method 'redisConnectionFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPoolConfig
问题原因

Spring Data Redis 的 Lettuce 连接池需要用到 Apache Commons Pool2 库,但 spring-boot-starter-data-redis 2.x 版本不会自动传递这个依赖。

解决方法

pom.xml 中添加 Apache Commons Pool2 依赖:

<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version>
</dependency>

添加依赖后,重新构建项目即可解决该问题。

问题二:Redis 连接被拒绝

在运行测试或启动应用时,遇到以下错误:

java.net.ConnectException: Connection refused: no further informationat sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) ~[na:1.8.0_451]
问题原因

这个错误通常表示无法连接到 Redis 服务器,可能的原因有:

  1. Redis 服务器未启动
  2. Redis 服务器运行在不同的主机或端口
  3. 防火墙阻止了连接
  4. Redis 配置中的连接信息不正确
解决方法
  1. 确保 Redis 服务器已启动:

    • Windows: 检查 Redis 服务是否在运行
    • Linux/Mac: 使用 redis-server 命令启动 Redis
  2. 验证 Redis 连接信息:

    • 检查 application.yml 中的 Redis 配置是否正确
    • 默认配置为 localhost:6379
  3. 使用 Redis 客户端测试连接:

    redis-cli ping
    

    如果返回 “PONG”,说明 Redis 服务器正常运行

  4. 检查防火墙设置:

    • 确保 Redis 端口(默认 6379)未被防火墙阻止
    • 如果使用远程 Redis,确保网络连接正常
  5. 如果使用测试配置:

    • 确保 application-test.yml 中的配置正确
    • 测试使用数据库 1,确保不会影响生产数据

8. 自问自答

1. 为什么 Redis 可以直接使用而不需要重启服务?

Redis 可以直接使用而不需要重启服务,主要是因为:

  • 连接池机制:Spring Boot 使用 Lettuce 连接池,允许应用程序在需要时获取和返回连接,而不需要每次都创建新的连接。
  • 长连接支持:Lettuce 是一个异步、非阻塞的客户端,支持长连接(Keep-Alive),能够自动重连。
  • 自动配置:Spring Boot 在启动时会自动配置 Redis 连接,当 Redis 服务可用时,连接会自动建立,并在连接丢失时尝试重连。
2. 如何确保 Redis 连接的安全性?

确保 Redis 连接的安全性可以通过以下方式:

  • 使用密码:在 Redis 配置中设置密码,确保只有授权用户才能访问。
  • 限制访问:通过防火墙或网络配置限制 Redis 的访问范围。
  • 加密传输:使用 SSL/TLS 加密 Redis 连接,防止数据被窃取。
  • 定期更新:保持 Redis 和 Spring Boot 的版本更新,以修复已知的安全漏洞。
3. 如何处理 Redis 连接失败的情况?

处理 Redis 连接失败的情况可以通过以下方式:

  • 重试机制:在连接失败时,实现重试逻辑,尝试重新连接。
  • 健康检查:定期检查 Redis 连接状态,确保连接正常。
  • 日志记录:记录连接失败的原因,便于后续分析和调试。
  • 降级策略:在 Redis 不可用时,实现降级策略,确保应用程序仍能正常运行。
4. 如何优化 Redis 的性能?

优化 Redis 的性能可以通过以下方式:

  • 使用连接池:配置合适的连接池大小,避免频繁创建和销毁连接。
  • 数据压缩:对于大对象,考虑使用压缩算法减少内存占用。
  • 合理设置过期时间:为缓存数据设置合理的过期时间,避免内存占用过高。
  • 使用 Redis 集群:对于高并发场景,考虑使用 Redis 集群分散负载。
5. 如何监控 Redis 的使用情况?

监控 Redis 的使用情况可以通过以下方式:

  • 使用监控工具:如 Redis Desktop Manager、Grafana 等工具监控 Redis 的性能和状态。
  • 日志记录:记录 Redis 的操作日志,便于分析使用情况。
  • 健康检查接口:实现健康检查接口,定期检查 Redis 连接状态。
  • 性能指标:收集 Redis 的性能指标,如内存使用、连接数等,便于优化。

参考资源

  1. 官方文档

    • Spring Boot 官方文档
    • Spring Data Redis 官方文档
    • Redis 官方文档
  2. 推荐书籍

    • 《Spring Boot 实战》
    • 《Redis 实战》
    • 《Spring Data Redis 实战》
  3. 在线教程

    • Spring Boot 教程
    • Spring Data Redis 教程
    • Redis 教程
  4. 工具资源

    • Spring Initializr
    • Redis Desktop Manager
    • Postman API 测试工具

总结

Spring Boot + Redis 脚手架提供了一个完整的缓存应用开发基础,包含了必要的配置和示例代码。通过这个项目,你可以:

  1. 快速搭建缓存应用
  2. 实现 Redis 操作
  3. 进行单元测试
  4. 使用开发工具

下期预告

在下一篇文章中,我们将介绍 Spring Boot + 安全框架项目脚手架,主要内容包括:

  1. Spring Security 基础配置
  2. 用户认证与授权
  3. JWT 令牌管理
  4. 安全过滤器链
  5. 权限控制实现
  6. 安全测试方案

敬请期待!

彩蛋:Redis 在 Windows 上的安装过程

1. 下载 Redis

  1. 访问 Redis 官方下载页面。
  2. 下载最新的 Redis for Windows 安装包(例如 Redis-x64-3.0.504.msi)。

2. 安装 Redis

  1. 双击下载的安装包,启动安装向导。
  2. 按照向导的提示进行安装,选择安装路径和其他选项。
  3. 完成安装后,Redis 服务会自动启动。

3. 验证安装

  1. 打开命令提示符(CMD)。
  2. 输入 redis-cli 命令,进入 Redis 命令行界面。
  3. 输入 ping 命令,如果返回 PONG,则表示 Redis 服务正常运行。

4. 配置 Redis

  1. 打开 Redis 安装目录下的 redis.windows.conf 文件。
  2. 根据需要修改配置,例如设置密码、更改端口等。
  3. 保存文件后,重启 Redis 服务以应用更改。

5. 使用 Redis

  1. 在应用程序中配置 Redis 连接信息,确保与 Redis 服务连接正常。
  2. 使用 Redis 客户端或命令行工具进行数据操作。

补充:
如果你未使用 Docker 环境,建议将测试代码中的 Testcontainers 相关内容(如 @Testcontainers、@Container、@DynamicPropertySource 等)移除,直接连接本地 Redis 服务。同时可在 pom.xml 中移除 testcontainers 相关依赖,避免无用报错。

相关文章:

  • 配置别名路径 @
  • ArcGIS切片方案记录bundle文件
  • 机器学习笔记3
  • 【iOS】alloc的实际流程
  • 106. 从中序与后序遍历序列构造二叉树
  • 本地化部署HomeAssistant语音助手并接入DeepSeek
  • 波导模型(表面等离激元、石墨烯等)本征模式分析、各种类型波导传输效率求解
  • JAVA数组题(7)
  • STL - stack 和 queue 及容器适配器模式的介绍
  • C++11(2)
  • 大语言模型三大演进方向:记忆增强、工具集成与多模态突破
  • 插件双更新:LeetCode 刷题支持正式上线,JetBrains IDE 插件持续升级!
  • 《从零开始入门递归算法:搜索与回溯的核心思想 + 剑指Offer+leetcode高频面试题实战(含可视化图解)》​
  • 机器学习第十三讲:独热编码 → 把“红黄蓝“颜色变成001/010/100的数字格式
  • 将.pt文件执行图像比对
  • 赛博放生:用数字技术重构心灵仪式
  • 跨系统数据烟囱如何破局?豪森智源HSMES重构制造协同新范式‌
  • AI全域智能监控系统重构商业清洁管理范式——从被动响应到主动预防的监控效能革命
  • 数据科学和机器学习的“看家兵器”——pandas模块 之四
  • 上线前测试组发现问题较多。开发总结
  • 网易一季度净利增长三成,丁磊:高度重视海外游戏市场
  • 把中国声音带向世界,DG和Blue Note落户中国
  • 布局50多个国家和地区,我国科技型企业孵化器数量全球第一
  • 一个多月来上海交大接连“牵手”三区,在这些方面进行区校合作
  • 最新研究:新型合成小分子可“精准杀伤”癌细胞
  • 硅料收储挺价“小作文”发酵光伏板块罕见大涨,知情人士:确实在谈