内置redis使用方法
在Spring Boot项目中,使用内置(嵌入式)Redis并通过注解实现缓存非常便捷。以下是具体实现步骤,主要利用Spring Cache抽象和嵌入式Redis的自动配置:
步骤1:引入依赖
在pom.xml
(Maven)或build.gradle
(Gradle)中添加相关依赖,主要包括Spring Cache、嵌入式Redis和Redis客户端:
Maven依赖:
<!-- Spring Cache抽象 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency><!-- Redis客户端(Lettuce) -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency><!-- 嵌入式Redis(用于开发测试) -->
<dependency><groupId>it.ozimov</groupId><artifactId>embedded-redis</artifactId><version>0.7.3</version><scope>test</scope> <!-- 建议仅在测试环境启用 -->
</dependency>
说明:embedded-redis
默认是测试环境依赖,生产环境需切换为独立Redis。
步骤2:配置嵌入式Redis
在application.yml
中配置Redis和缓存相关参数,指定使用嵌入式Redis:
spring:cache:type: redis # 缓存类型为Redisredis:time-to-live: 600000 # 缓存默认过期时间(10分钟,单位毫秒)cache-null-values: false # 不缓存null值,避免缓存穿透use-key-prefix: true # 使用缓存键前缀,防止键冲突redis:host: localhostport: 6379database: 0# 嵌入式Redis配置(仅在引入embedded-redis依赖时生效)embedded:enabled: true # 启用嵌入式Redisport: 6379 # 嵌入式Redis端口(默认6379)
步骤3:启用缓存注解
在Spring Boot启动类上添加@EnableCaching
注解,开启缓存功能:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication
@EnableCaching // 启用缓存注解
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
步骤4:使用缓存注解
通过Spring提供的缓存注解(如@Cacheable
、@CachePut
、@CacheEvict
)实现缓存逻辑,无需手动编写Redis操作代码。
示例Service层代码:
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
public class UserService {// 1. 查询数据时缓存:@Cacheable// 当调用该方法时,先查缓存;若缓存不存在,执行方法并将结果存入缓存@Cacheable(value = "userCache", key = "#id") // value:缓存名称;key:缓存键(支持SpEL表达式)public User getUserById(Long id) {// 模拟数据库查询(实际中是DAO层调用)System.out.println("从数据库查询用户:" + id);return new User(id, "用户" + id, 20);}// 2. 更新数据时更新缓存:@CachePut// 执行方法后,将结果更新到缓存(适合新增/修改操作)@CachePut(value = "userCache", key = "#user.id")public User updateUser(User user) {// 模拟数据库更新System.out.println("更新数据库用户:" + user.getId());return user;}// 3. 删除数据时清除缓存:@CacheEvict// 执行方法后,删除指定缓存@CacheEvict(value = "userCache", key = "#id")public void deleteUser(Long id) {// 模拟数据库删除System.out.println("删除数据库用户:" + id);}// 4. 清除整个缓存(如批量删除场景)@CacheEvict(value = "userCache", allEntries = true)public void clearAllUsers() {// 模拟批量删除System.out.println("清空所有用户数据");}
}
实体类User:
import lombok.AllArgsConstructor;
import lombok.Data;@Data
@AllArgsConstructor
public class User {private Long id;private String name;private Integer age;
}
步骤5:测试缓存效果
编写Controller或测试类验证缓存是否生效:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/users")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/{id}")public User getUser(@PathVariable Long id) {return userService.getUserById(id);}@PutMappingpublic User updateUser(@RequestBody User user) {return userService.updateUser(user);}@DeleteMapping("/{id}")public String deleteUser(@PathVariable Long id) {userService.deleteUser(id);return "删除成功";}
}
测试现象:
- 首次调用
/users/1
:控制台输出“从数据库查询用户:1”(缓存未命中)。 - 再次调用
/users/1
:控制台无输出(直接从缓存获取)。 - 调用
PUT /users
更新用户:缓存会同步更新。 - 调用
DELETE /users/1
:缓存中该用户数据会被清除。
核心注解说明
@Cacheable
:查询时缓存,优先从缓存获取,无缓存则执行方法并缓存结果。@CachePut
:更新时同步缓存,执行方法后将结果存入缓存(常用于新增/修改)。@CacheEvict
:删除时清除缓存,支持删除单个键(key
)或整个缓存(allEntries = true
)。@Caching
:组合多个缓存操作(如同时更新和清除多个缓存)。
注意事项
- 环境区分:嵌入式Redis仅建议在开发/测试环境使用,生产环境需切换为独立Redis服务(只需移除
embedded-redis
依赖,配置独立Redis的host
和port
即可)。 - 缓存键生成:默认键生成策略为
缓存名称::方法参数
,可通过key
属性自定义(使用SpEL表达式,如#user.id
、#root.methodName
等)。 - 序列化方式:默认使用JDK序列化,可自定义为JSON(如
Jackson2JsonRedisSerializer
),避免序列化问题。 - 过期时间:通过
spring.cache.redis.time-to-live
设置全局过期时间,也可在@Cacheable
中通过unless
或自定义配置指定单个缓存的过期时间。
通过以上步骤,即可快速实现基于注解的嵌入式Redis缓存,简化开发流程的同时保证缓存逻辑的清晰性。
@EnableCaching
是 Spring 框架自带的注解(属于 org.springframework.cache.annotation
包),它的核心作用是开启 Spring 的缓存管理功能,底层已经内置了切面(AOP)处理逻辑,无需你手动实现切面。
具体说明:
-
@EnableCaching
的作用
该注解通过导入CachingConfigurationSelector
类,自动注册了缓存相关的 AOP 切面(CacheInterceptor
)和缓存管理器(CacheManager
)。
这些内置组件会拦截被@Cacheable
、@CachePut
等注解标记的方法,在方法执行前后自动完成缓存的查询、更新、删除等操作,完全无需你手动编写切面代码。 -
无需手动处理切面的原因
Spring 缓存抽象的设计理念是**“声明式缓存”**:- 你只需通过注解(
@Cacheable
等)声明“哪些方法需要缓存”“缓存规则是什么”,底层的 AOP 逻辑由 Spring 自动实现。 - 切面会在方法执行前检查缓存是否存在,存在则直接返回缓存结果(跳过方法执行);不存在则执行方法,再将结果存入缓存(对应
@Cacheable
)。 - 对于
@CachePut
@CacheEvict
等注解,切面会在方法执行后自动触发缓存更新或清除操作。
- 你只需通过注解(
-
自定义扩展的可能性
虽然无需手动写切面,但你可以通过以下方式自定义缓存行为:- 自定义
KeyGenerator
改变缓存键的生成规则; - 自定义
CacheManager
配置缓存的序列化方式、过期时间等; - 实现
Cache
接口扩展自定义缓存逻辑(如结合 Redis 高级特性)。
- 自定义
总结:
@EnableCaching
是 Spring 自带的注解,已内置切面处理逻辑,你只需在启动类上添加该注解,并在方法上使用 @Cacheable
等注解,即可实现缓存功能,无需手动编写 AOP 切面代码。这种声明式设计极大简化了缓存集成的复杂度。
要理解@Async
的作用和优势,我们可以从“同步执行”的问题入手,对比来看异步的价值。
一、先看“同步执行”的痛点
假设你有一个接口,需要完成3件事:
- 保存用户数据到数据库(耗时50ms)
- 发送欢迎邮件给用户(耗时1000ms,因为涉及网络请求)
- 记录操作日志到文件(耗时20ms)
如果用同步执行(默认方式),代码会按顺序执行:
public void register(User user) {saveToDB(user); // 50mssendEmail(user); // 1000ms(耗时最长)logOperation(user); // 20ms
}
总耗时 = 50 + 1000 + 20 = 1070ms
此时,用户会感受到明显的卡顿(超过1秒),因为必须等所有步骤完成才能得到响应。
二、@Async
的作用:让任务“并行执行”
用@Async
标记耗时的步骤(如发送邮件),让它在独立线程中执行,不阻塞主线程:
public void register(User user) {saveToDB(user); // 主线程执行(50ms)asyncSendEmail(user); // 异步执行(扔给线程池,不等待)logOperation(user); // 主线程继续执行(20ms)
}@Async // 异步方法:在独立线程中执行
public void asyncSendEmail(User user) {sendEmail(user); // 1000ms(在其他线程执行)
}
总耗时 ≈ 50 + 20 = 70ms
主线程只需执行快速任务(存数据库、打日志),耗时操作(发邮件)在后台线程跑,用户几乎感觉不到卡顿。
三、异步的核心优势
-
提升用户体验
避免用户等待耗时操作(如邮件、短信、文件上传),接口响应速度大幅提升(从秒级到毫秒级)。 -
提高系统吞吐量
同步执行时,一个请求会占用线程直到完成,高并发下线程会被耗尽(如1000个请求同时发邮件,线程全被阻塞)。
异步执行时,主线程快速释放,可处理更多请求,而耗时操作由线程池统一管理,资源利用率更高。 -
解耦操作步骤
非核心流程(如日志、统计)可异步执行,不影响核心业务(如订单提交),即使异步操作失败,也不会导致主流程出错。
四、哪些场景必须用异步?
-
耗时的IO操作:
- 发送邮件/短信(调用第三方API,网络耗时)
- 文件上传下载(磁盘IO耗时)
- 调用外部接口(如支付回调、数据同步)
-
非实时必要的操作:
- 日志记录、数据统计(用户不需要等待这些完成)
- 消息通知(如“订单已发货”,晚几秒发送不影响)
-
高并发场景:
- 秒杀、抢购(必须快速响应,后续逻辑异步处理)
- 批量处理(如批量导出数据,前端先返回“处理中”,后端异步执行)
总结
@Async
的本质是**“让耗时操作在后台偷偷跑,不耽误主线程干活”**。
它解决了同步执行时“一人干活、大家等待”的问题,通过多线程并行处理,显著提升系统的响应速度和并发能力,是处理高并发、优化用户体验的核心手段之一。