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

Spring Cache 详细介绍——补充

Spring Cache 是 Spring Framework 提供的一个强大的缓存抽象层,它允许开发者通过简单的注解方式将缓存功能集成到应用程序中,而无需关心底层缓存实现的细节。下面我将从多个方面详细介绍 Spring Cache。

一、核心概念

Spring Cache 基于以下几个核心概念构建:

  1. 缓存抽象接口:Spring 定义了一套缓存抽象接口,如 Cache 和 CacheManager,使应用能够与各种缓存实现进行交互。

  2. 声明式缓存:通过注解(如 @Cacheable@CachePut@CacheEvict 等)来声明缓存行为,无需编写复杂的缓存逻辑代码。

  3. 缓存管理器CacheManager 接口负责创建、配置和管理缓存实例。

  4. 缓存解析器CacheResolver 接口用于确定在特定操作中应该使用哪些缓存。

  5. 键生成器KeyGenerator 接口用于生成缓存键,默认实现使用方法参数。

二、常用注解

Spring Cache 提供了几个核心注解:

  1. @Cacheable:标记方法的返回值应该被缓存。如果缓存中已存在相同的键,则直接返回缓存值,否则执行方法并缓存结果。

  2. @CachePut:强制执行方法并将结果放入缓存,不考虑缓存中是否已存在相同的键。

  3. @CacheEvict:标记方法用于从缓存中移除数据。

  4. @Caching:组合多个缓存注解。

  5. @CacheConfig:在类级别设置缓存配置。

三、缓存管理器实现

Spring Cache 支持多种缓存管理器实现:

  • SimpleCacheManager:使用简单的内存 Map 实现,主要用于测试。
  • ConcurrentMapCacheManager:使用 ConcurrentHashMap 作为缓存存储。
  • EhCacheCacheManager:与 EhCache 集成。
  • RedisCacheManager:与 Redis 集成。
  • CaffeineCacheManager:与 Caffeine 集成。
  • JCacheCacheManager:与 JSR-107 (JCache) 兼容的缓存提供程序集成。

四、基本配置示例

下面是一个使用 Spring Boot 和 Redis 配置 Spring Cache 的示例:

controller:


@RestController
@RequestMapping("/api/users")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("/{id}")public ResponseEntity<?> getUser(@PathVariable Long id) {Optional<User> user = userService.getUserById(id);return user.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());}@PostMappingpublic ResponseEntity<User> saveUser(@RequestBody User user) {return ResponseEntity.ok(userService.saveUser(user));}@DeleteMapping("/{id}")public ResponseEntity<Void> deleteUser(@PathVariable Long id) {userService.deleteUser(id);return ResponseEntity.noContent().build();}@DeleteMapping("/cache")public ResponseEntity<Void> clearCache() {userService.clearAllUserCache();return ResponseEntity.noContent().build();}
}

service

@Service
public class UserService {@Autowiredprivate UserRepository userRepository;// 缓存注解示例@Cacheable(value = "users", key = "#id", unless = "#result == null")public Optional<User> getUserById(Long id) {return userRepository.findById(id);}@CachePut(value = "users", key = "#user.id")public User saveUser(User user) {return userRepository.save(user);}@CacheEvict(value = "users", key = "#id")public void deleteUser(Long id) {userRepository.deleteById(id);}// 清空所有用户缓存@CacheEvict(value = "users", allEntries = true)public void clearAllUserCache() {// 方法体为空,仅用于触发缓存清除}
}

Repository:

@Repository
public class UserRepository {private final Map<Long, User> userMap = new HashMap<>();// 初始化一些测试数据public UserRepository() {userMap.put(1L, new User(1L, "张三", 25));userMap.put(2L, new User(2L, "李四", 30));userMap.put(3L, new User(3L, "王五", 35));}public Optional<User> findById(Long id) {System.out.println("从数据库查询用户: " + id);return Optional.ofNullable(userMap.get(id));}public User save(User user) {userMap.put(user.getId(), user);System.out.println("保存用户到数据库: " + user);return user;}public void deleteById(Long id) {userMap.remove(id);System.out.println("从数据库删除用户: " + id);}
}

model

public class User implements Serializable {private Long id;private String name;private Integer age;// 构造函数、getter和setter方法public User() {}public User(Long id, String name, Integer age) {this.id = id;this.name = name;this.age = age;}// getter和setter方法public Long getId() { return id; }public void setId(Long id) { this.id = id; }public String getName() { return name; }public void setName(String name) { this.name = name; }public Integer getAge() { return age; }public void setAge(Integer age) { this.age = age; }@Overridepublic String toString() {return "User{id=" + id + ", name='" + name + "', age=" + age + '}';}
}

DemoApplication.java

@SpringBootApplication
@EnableCaching // 启用缓存功能
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}

application.yml

spring:redis:host: localhostport: 6379cache:type: redis

UserServiceTest.java

package com.example.demo;import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.Optional;import static org.junit.jupiter.api.Assertions.*;@SpringBootTest
class UserServiceTest {@Autowiredprivate UserService userService;@Testvoid testGetUserById_CacheHit() {// 第一次调用 - 应该从数据库获取Optional<User> user1 = userService.getUserById(1L);assertTrue(user1.isPresent());assertEquals("张三", user1.get().getName());// 第二次调用 - 应该从缓存获取Optional<User> user2 = userService.getUserById(1L);assertTrue(user2.isPresent());assertEquals("张三", user2.get().getName());// 验证两次获取的是同一个对象(缓存命中)assertSame(user1.get(), user2.get());}@Testvoid testSaveUser() {// 创建新用户User newUser = new User(4L, "赵六", 40);// 保存用户,应该触发缓存更新User savedUser = userService.saveUser(newUser);assertEquals("赵六", savedUser.getName());// 获取用户,应该从缓存中获取Optional<User> retrievedUser = userService.getUserById(4L);assertTrue(retrievedUser.isPresent());assertEquals("赵六", retrievedUser.get().getName());}@Testvoid testDeleteUser() {// 确保用户存在Optional<User> user = userService.getUserById(1L);assertTrue(user.isPresent());// 删除用户,应该触发缓存清除userService.deleteUser(1L);// 再次获取用户,应该从数据库获取且不存在Optional<User> deletedUser = userService.getUserById(1L);assertFalse(deletedUser.isPresent());}
}

五、注解详解

1.@Cacheable

这个注解标记方法的返回值应该被缓存。常用属性:

  • value/cacheNames:指定缓存名称。
  • key:指定缓存键,可以使用 SpEL 表达式。
  • condition:指定缓存条件,使用 SpEL 表达式。
  • unless:指定不缓存的条件,使用 SpEL 表达式。
  • sync:是否同步缓存,防止缓存击穿。

2.@CachePut

这个注解标记方法的返回值应该被更新到缓存中,无论缓存中是否已存在该键。常用属性与@Cacheable相同。

3.@CacheEvict

这个注解标记方法用于从缓存中移除数据。常用属性:

  • value/cacheNames:指定缓存名称。
  • key:指定要移除的缓存键。
  • allEntries:是否移除所有缓存项。
  • beforeInvocation:是否在方法执行前移除缓存。

4.@Caching

这个注解用于组合多个缓存注解。

5.@CacheConfig

这个注解用于类级别,设置缓存的默认配置。

六、高级特性

1.自定义键生成器

可以通过实现KeyGenerator接口来自定义缓存键的生成策略:

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Component("myKeyGenerator")
public class MyKeyGenerator implements KeyGenerator {@Overridepublic Object generate(Object target, Method method, Object... params) {// 自定义键生成逻辑return target.getClass().getSimpleName() + "_" + method.getName() + "_" + String.join("_", params);}
}

2.自定义缓存解析器

可以通过实现CacheResolver接口来自定义缓存解析策略。

3.条件缓存

使用conditionunless属性可以实现条件缓存:

@Cacheable(value = "users", key = "#id", condition = "#id > 0", unless = "#result == null")
public User getUserById(Long id) {return userRepository.findById(id).orElse(null);
}

4.缓存同步

使用sync属性可以防止缓存击穿:

@Cacheable(value = "users", key = "#id", sync = true)
public User getUserById(Long id) {// 当缓存不存在时,只有一个线程会执行此方法return userRepository.findById(id).orElse(null);
}

http://www.xdnf.cn/news/13981.html

相关文章:

  • 深度解析 JuiceFS 权限管理:Linux 多种安全机制全兼容
  • JavaScript 原型(Prototype)详解
  • 金属切削机床制造企业如何破局?探索项目管理数字化转型
  • 学习STC51单片机35(芯片为STC89C52RCRC)智能小车3(PWM调速小车)
  • C++学习之虚析构函数
  • 【QT】QVariant 转换为自定义的枚举类型
  • 当机械工程师的餐桌变身实验室:立创电赛的真实创新启示录
  • 《深度优先搜索》题集
  • 结构型模式 (7种)
  • 鸿蒙期末总结
  • 高铁列车能否考虑加装飞翼?
  • 深入剖析 C++ 默认函数:拷贝构造与赋值运算符重载
  • C# TAP异步编程(Task/async/await)总结
  • VRFF: Video Registration and Fusion Framework
  • 机器学习与深度学习20-数学优化
  • 2025.06.12【3D曲线图】|用Python绘制DNA甲基化3D曲线图(以CpG位点为例)
  • 局域网内 100 台设备同屏直播技术方案
  • 【总天数两种算法相互印证正确】2022-4-13
  • flowable查询历史流程实例时条件变量的类型问题
  • 实战解析:如何用克魔(KeyMob)等工具构建iOS应用稳定性与数据可观测体系
  • 【web应用】若依框架:若依框架中的面包屑导航与顶部导航栏:设计与实现
  • 电感详解同时其主要特性参数是什么?都有涉及哪些方面?
  • Wireshark 的基本使用
  • vulnyx Exec writeup
  • C++内存管理与编译链接
  • 芯片制程变化
  • centos 7.9 升级ssh版本 7.4p1 升级到 8.2p1
  • Spring AI Chat Client API 指南
  • uni-app项目实战笔记2--使用swiper实现纵向轮播图
  • 常见数据结构