SpringBoot整合SpringCache使用缓存
文章目录
-
- SpringBoot整合SpringCache使用缓存
- 1.介绍
- 2.SpringBoot整合
-
- 1.导入xml依赖
- 2.配置yml
- 3.使用@EnableCaching启用SpringCache
- 4.@Cacheable
- 5.@CachePut
- 6.@CacheEvict
- [7. @Caching](#7. @Caching)
- 8.@CacheConfig
- 3.其他属性配置
-
-
- [1.`keyGenerator` 属性](#1.
keyGenerator
属性) - [2. `cacheManager` 属性](#2.
cacheManager
属性) - [3.`cacheResolver` 属性](#3.
cacheResolver
属性) - 4.CacheManagerCustomizer
- [1.`keyGenerator` 属性](#1.
-
1.介绍
Spring Cache 提供了 Cache
和 CacheManager
接口来统一管理不同的缓存技术。Cache
是缓存的抽象,CacheManager
负责管理多个 Cache
实例。Spring Cache 支持多种缓存实现,包括:
- ConcurrentHashMap:默认的缓存实现,适用于简单的本地缓存。
- Redis:基于 Redis 的分布式缓存,适用于高并发场景。
- Ehcache:符合 JSR-107 标准的缓存实现,支持二级缓存。
- Caffeine:基于 Java 8 的高性能缓存库,适用于需要高性能的场景。
- JSR-107:支持 JSR-107 标准的缓存实现。
2.SpringBoot整合
本文基于springboot2.7版本测试
1.导入xml依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置yml
yml
spring:
cache:
cache-names: user
type: redis
redis:
#缓存前缀
key-prefix: moshangshang_
#是否启用缓存统计信息。
enable-statistics: false
#是否允许缓存 null 值。
cache-null-values: true
#写入 Redis 时是否使用 key prefix。
use-key-prefix: true
redis:
port: 6379
host: 127.0.0.1
password: root
lettuce:
pool:
max-active: 20 #连接池最大连接数(使用负值表示没有限制)
max-idle: 8 #连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
timeout: 6000 #连接超时时长(毫秒)
如果cache-null-values:属性启用不能缓存null值,则缓存null时会抛出下方异常
shell
java.lang.IllegalArgumentException: Cache 'user' does not allow 'null' values. Avoid storing null via '@Cacheable(unless="#result == null")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.
cache-names属性说明:
用于管理全的缓存key的全局配置,多个用逗号分割,比如 cache-names: user; use-key-prefix: false,则表示 @Cacheable(cacheNames = "user")之类的注解不会使用key-prefix指定的缓存前缀,未配置的缓存名称则采用默认全局配置
3.使用@EnableCaching启用SpringCache
java
@SpringBootApplication
@EnableCaching
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
4.@Cacheable
@Cacheable 用于标记方法或类,表示该方法的返回值可以被缓存。
当方法执行前,Spring 会检查缓存中是否存在相同 key 的缓存元素,如果存在则直接返回,否则执行方法并将结果存入缓存。
@Cacheable 的方法必须为 public:如果方法不是 public 的,Spring 无法通过代理来访问缓存。
- value 或 cacheNames:指定缓存的名称,可以是单个字符串或字符串数组。
- key:指定缓存的键,可以使用 SpEL 表达式来定义。
- condition:指定缓存的条件,只有当条件为 true 时,才会从缓存中获取结果。
- unless:指定不将结果放入缓存的条件,即使结果存在,也不会被缓存。
- sync:是否使用同步方式获取缓存,避免多个线程同时执行方法。
- 在某些场景下,需要确保缓存和数据库的一致性,可以使用 @Cacheable 的 sync 属性来启用同步更新。且在多线程环境下确保只有一个线程执行查询。
java
/**
* 根据id查询用户信息
* 生成的key为moshangshang_user::38
*/
@GetMapping("/query/{id}")
@Cacheable(cacheNames = "user",key = "#id",unless = "#result == null")
public User getById(@PathVariable Long id){
return userService.getById(id);
}
5.@CachePut
@CachePut 用于标记方法,表示每次调用该方法时都会执行并存入缓存。
它总是会执行方法,并将结果添加到缓存中,不会检查缓存中是否存在相同 key 的缓存元素。
- value 或 cacheNames:指定缓存的名称,可以是单个字符串或字符串数组。
- key:指定缓存的键,可以使用 SpEL 表达式来定义。
- condition:指定缓存的条件,只有当条件为 true 时,才会从缓存中获取结果。
- unless:指定不将结果放入缓存的条件,即使结果存在,也不会被缓存。
java
@PostMapping("/save")
@CachePut( key = "#user.id")
public User updateUser(User user) {
userService.saveOrUpdate(user);
return user;
}
6.@CacheEvict
@CacheEvict 用于标记方法,表示该方法执行时会清除缓存中的数据。
@CacheEvict 在方法执行期间抛出异常不会清空缓存:如果方法执行过程中抛出异常,@CacheEvict 的 allEntries 属性不会生效。
它可以用于删除缓存中的所有键值对,也可以用于清除特定的 key。
- value 或 cacheNames:指定缓存的名称,可以是单个字符串或字符串数组。
- key:指定缓存的键,可以使用 SpEL 表达式来定义。
- condition:指定缓存的条件,只有当条件为 true 时,才会从缓存中获取结果。
- beforeInvocation:是否在方法执行前清除缓存,为 true 时在方法执行前清除缓存。
- allEntries:是否清除所有缓存条目,为 true 时清除所有缓存。
java
/**
* @CacheEvict 在方法执行期间抛出异常不会清空缓存:如果方法执行过程中抛出异常,@CacheEvict 的 allEntries 属性不会生效。
* @CacheEvict 用于标记方法,表示该方法执行时会清除缓存中的数据。
* 它可以用于删除缓存中的所有键值对,也可以用于清除特定的 key。
* value 或 cacheNames:指定要清除的缓存名称。
* key:指定要清除的缓存键,可以使用 SpEL 表达式来定义。
* allEntries:是否清除所有缓存条目,为 true 时清除所有缓存。
* beforeInvocation:是否在方法执行前清除缓存,为 true 时在方法执行前清除缓存。
* condition:指定清除缓存的条件,只有当条件为 true 时,才会清除缓存。
*/
@GetMapping("/delete/{id}")
@CacheEvict(key = "#id", allEntries = false)
public void deleteUser(@PathVariable Long id) {
userService.removeById(id);
}
7. @Caching
@Caching 是一个组合注解,可以同时应用多个其他注解,表示该方法会同时执行 @Cacheable、@CachePut 和 @CacheEvict 的操作。
java
@GetMapping("/save/caching")
@Caching(
cacheable = @Cacheable( key = "#user.id"),
put = @CachePut( key = "#user.id"),
evict = @CacheEvict( key = "#user.id")
)
public User saveUser(User user) {
userService.save(user);
return user;
}
8.@CacheConfig
@CacheConfig 用于在类上设置公共的缓存配置,避免在每个方法上重复配置。
java
/**
* @CacheConfig 用于在类上设置公共的缓存配置,避免在每个方法上重复配置。
*/
@RestController
@AllArgsConstructor
@RequestMapping("cache")
@CacheConfig(cacheNames = "user")
public class CacheController {
private final IUserService userService;
@GetMapping("/query/{id}")
@Cacheable(key = "#id",unless = "#result == null")
public User getById(@PathVariable Long id){
return userService.getById(id);
}
}
3.其他属性配置
1.keyGenerator
属性
keyGenerator
属性用于指定默认的键生成器(Key Generator)。如果在方法上未显式指定 key
属性,则使用该属性值作为默认的键生成器。
1.配置生成器
java
@Configuration
public class CacheConfig {
@Bean(name = "customKeyGenerator")
public KeyGenerator keyGenerator() {
return (target, method, params) -> method.getName() + "[" + Arrays.asList(params) + "]";
}
}
等同于
java
@Bean(name = "customKeyGenerator")
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName() + "[" + Arrays.asList(params) + "]";
}
};
}
2.使用
生成的key为moshangshang_user::getById[[39]]
java
@Cacheable(unless = "#result == null",keyGenerator = "customKeyGenerator")
public User getById(@PathVariable Long id){
return userService.getById(id);
}
2. cacheManager
属性
cacheManager
属性用于指定当前使用的 CacheManager
实现类的名称。CacheManager
是 Spring 定义的一个接口,用于管理缓存(Cache)的创建和配置。Spring 提供了多种 CacheManager
的实现,例如 ConcurrentMapCacheManager
、EhCacheCacheManager
、CaffeineCacheManager
等。通过设置 cacheManager
属性,可以指定使用哪种缓存管理器来管理缓存。
创建自定义的缓存管理器
java
@Configuration
public class CacheConfig {
@Bean
public ConcurrentMapCacheManager mapCacheManager() {
return new ConcurrentMapCacheManager("user-map","user");
}
}
cacheManager()
方法返回了一个 ConcurrentMapCacheManager
实例,并且指定了缓存名称。这个 CacheManager
将被用于管理名为 指定的缓存。
java
/**
* 执行的是mapCacheManager的缓存管理器
*/
@GetMapping("/manger/map/query/{id}")
@Cacheable(cacheManager = "mapCacheManager")
public User getRedisMangerById(@PathVariable Long id){
return userService.getById(id);
}
3.cacheResolver
属性
cacheResolver
属性用于指定一个自定义的 CacheResolver
实现。默认情况下,Spring 使用 SimpleCacheResolver
来解析缓存操作。通过自定义 CacheResolver
,可以实现更复杂的缓存逻辑,例如根据方法名动态选择缓存名称或缓存管理器。
cacheManager
和cacheResolver
是互斥的 :如果同时指定了cacheManager
和cacheResolver
,Spring 会抛出异常,因为CacheResolver
的实现会忽略自定义的CacheManager
。- 自定义
CacheResolver
的灵活性 :通过自定义CacheResolver
,可以实现更灵活的缓存管理,例如根据方法名、参数或上下文动态选择缓存名称或缓存管理器 。 - Spring 4.1 及以上版本 :从 Spring 4.1 开始,
@Cacheable
、@CachePut
、@CacheEvict
等注解的value
属性不再是强制性的,因为CacheResolver
可以提供缓存名称信息
自定义缓存解析器
getById方法取user缓存名称下数据,其他取user-map下数据
java
public class CustomCacheResolver implements CacheResolver {
private final ConcurrentMapCacheManager concurrentMapCacheManager;
private final RedisCacheManager redisCacheManager;
public CustomCacheResolver(ConcurrentMapCacheManager concurrentMapCacheManager,
RedisCacheManager redisCacheManager) {
this.concurrentMapCacheManager = concurrentMapCacheManager;
this.redisCacheManager = redisCacheManager;
}
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
Collection<Cache> caches = new ArrayList<>();
if (context.getTarget().getClass() == CacheController.class) {
if (context.getMethod().getName().equals("getRedisById")) {
caches.add(redisCacheManager.getCache("user"));
}else {
caches.add(concurrentMapCacheManager.getCache("user-map"));
}
}
return caches;
}
}
配置自定义缓存管理器并注册缓存解析器
配置了自定义的CacheManager会导致yml里面的相关配置失效(任何一个都会,比如指定map的缓存管理器,yml配redis,则redis的配置也不生效)
java
@Bean
public ConcurrentMapCacheManager mapCacheManager() {
return new ConcurrentMapCacheManager("user-map","user");
}
@Primary
@Bean
public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate) {
return RedisCacheManager.builder(Objects.requireNonNull(redisTemplate.getConnectionFactory()))
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 设置默认缓存过期时间为10分钟
.disableCachingNullValues()) // 禁用缓存空值
.withInitialCacheConfigurations(initialCacheConfigurations()) // 设置特定缓存的配置
.build();
}
private Map<String, RedisCacheConfiguration> initialCacheConfigurations() {
Map<String, RedisCacheConfiguration> initialConfigurations = new HashMap<>();
// 设置特定缓存的过期时间
initialConfigurations.put("user", RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 设置特定缓存的过期时间为1小时
.disableCachingNullValues());
return initialConfigurations;
}
@Bean
public CacheResolver customCacheResolver(ConcurrentMapCacheManager concurrentMapCacheManager,
RedisCacheManager redisCacheManager) {
return new CustomCacheResolver(concurrentMapCacheManager,redisCacheManager);
}
测试
java
/**
* 执行的是RedisCacheManager的缓存管理器
*/
@GetMapping("/resolver/redis/query/{id}")
@Cacheable(unless = "#result == null",cacheResolver = "customCacheResolver")
public User getRedisById(@PathVariable Long id){
return userService.getById(id);
}
/**
* 执行的是ConcurrentMapCacheManager的缓存管理器
*/
@GetMapping("/resolver/map/query/{id}")
@Cacheable(cacheNames = "user-map",unless = "#result == null",cacheResolver = "customCacheResolver")
public User getMapCacheById(@PathVariable Long id){
return userService.getById(id);
}
4.CacheManagerCustomizer
CacheManagerCustomizer
是一个用于在缓存管理器初始化之前对其进行自定义配置的接口。通过实现该接口的 Bean,可以对缓存管理器进行定制,例如设置缓存名称、是否允许缓存空值、设置缓存过期时间等。
- 自定义缓存配置 :
CacheManagerCustomizer
允许在缓存管理器初始化之前对缓存进行配置,例如设置缓存名称、过期时间、序列化方式等。这使得开发者可以针对不同的缓存需求进行灵活配置。 - 覆盖默认配置 :如果使用了
CacheManagerCustomizer
,那么application.yml
或application.properties
中的缓存配置将不会生效,因为CacheManagerCustomizer
会覆盖默认的配置 。 - 支持多种缓存类型 :
CacheManagerCustomizer
不仅适用于ConcurrentMapCacheManager
,还可以用于其他类型的缓存管理器,如RedisCacheManager
、CaffeineCacheManager
等。通过实现CacheManagerCustomizer
接口,可以对不同类型的缓存管理器进行统一的配置 。 - 提供回调机制 :
CacheManagerCustomizer
提供了一个customize
方法,该方法会在缓存管理器初始化之前被调用,允许开发者对缓存管理器进行定制。例如,可以设置缓存名称、允许或禁止缓存空值等 。
java
@Bean
public CacheManagerCustomizer<ConcurrentMapCacheManager> cacheManagerCustomizer() {
return new CacheManagerCustomizer<ConcurrentMapCacheManager>() {
@Override
public void customize(ConcurrentMapCacheManager cacheManager) {
cacheManager.setCacheNames(Arrays.asList("user"));
cacheManager.setAllowNullValues(false); // 禁用缓存空值
}
};
}
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return (builder) -> {
builder.cacheDefaults(
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 默认过期时间为 10 分钟
.disableCachingNullValues()); // 禁用缓存空值
};
}
如果配置了自定义的缓存管理器(redisCacheManager),则CacheManagerCustomizer将不生效