Spring Cache

Lu Lv3

Spring Cache

Spring Cache 是一个框架,实现了基于 注解 的缓存功能,只需要简单地加一个注解,就能实现缓存功能。

Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如:

  • EHCache
  • Caffeine
  • Redis

快速开始

引入Maven依赖

  1. Spring
1
2
3
4
5
6
7
8
9
10
11
12
<!-- Spring Cache -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.3</version> <!-- 请使用最新的稳定版本 或者 不填版本,让Spring管理版本 -->
</dependency>
<!--提供了更多由 EhCache 或 Caffeine 支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.3.3</version> <!-- 请使用最新的稳定版本 或者 不填版本,让Spring管理版本 -->
</dependency>

**注意:**如果引入了 pring-context-support 依赖,则通常不需要再依赖 spring-context,因为前者已经包含了后者

  1. Spring Boot
1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.7.3</version> <!-- 请使用最新的稳定版本 或者 不填版本,让Spring管理版本 -->
</dependency>

该 Starter 包含了 spring-context-support 模块。

启用缓存

只需要再任何配置类中添加 @EnableCaching 注解,即可启用缓存功能(推荐在启动类或者相关配置类上):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
@EnableCaching
public class RedisCacheConfig {

@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 设置缓存默认过期时间10分钟
.serializeKeysWith(RedisSerializationContext
.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext
.SerializationPair.
fromSerializer(new GenericJackson2JsonRedisSerializer()));

return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
}
}

在Spring中,启用缓存后,必须注册一个 cacheManager,这是最基本的设置。

在使用 Spring Boot 时,只需在 classpath 上存在 Starter 依赖,并且与 @EnableCaching 注解一起使用,就会注册相同的 ConcurrentMapCacheManager,因此不需要单独的 Bean 声明。还可以使用一个或多个 CacheManagerCustomizer<T> Bean 来自定义 自动配置CacheManager

常用注解

注解说明
@EnableCaching开启缓存注解功能,通常加在启动类或者相关的配置类上
@Cacheable在方法执行前查询缓存中是否有数据,如果有数据,则直接返回缓存数据;
如果没有缓存数据,调用方法并将方法返回值放到缓存中
@CachePut将方法的返回值放到缓存中
@CacheEvict将一条或多条数据从缓存中删除
@CacheConfig统一配置本类的缓存注解的属性
@Caching组合多个 Cache 注解使用

@Cacheable/@CachePut/@CacheEvict 主要的参数

image-20241111001419032

SpEL上下文数据

image-20241111001659625

注意:

1.当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如:

  • @Cacheable(key = "targetClass + methodName +#p0")

2.使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:

  • @Cacheable(value=”users”, key=”#id”)
  • @Cacheable(value=”users”, key=”#p0”)

在实现不同缓存时,SpringCache缓存键值的选取

  • Redis:Spring Cache 在 Redis 中存储的键会是 cacheName::key 的格式,例如 users::123

  • Caffeine:Spring Cache 在 Caffeine 中,通常直接将 @Cacheablekey(方法参数)作为缓存的键存储。如 123(没有 cacheName:: 前缀)。

SpEL提供了多种运算符

image-20241111002402577

注解讲解

@CachePut、@Cacheable

image-20241111002636009

@CacheEvict

主要针对方法配置,能够根据一定的条件对缓存进行清空 。

image-20241111002856159

image-20241111002911173
@CacheConfig

当我们需要缓存的地方越来越多,你可以使用@CacheConfig(cacheNames = {"myCache"})注解来统一指定value的值,这时可省略value,如果你在你的方法依旧写上了value,那么依然以方法的value值为准。

1
2
3
4
5
6
7
8
9
@CacheConfig(cacheNames = {"myCache"})
public class BotRelationServiceImpl implements BotRelationService {
@Override
@Cacheable(key = "targetClass + methodName +#p0")//此处没写value
public List<BotRelation> findAllLimit(int num) {
return botRelationRepository.findAllLimit(num);
}
.....
}
@Caching

有时候我们可能组合多个Cache注解使用,此时就需要@Caching组合多个注解标签了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Caching(cacheable = {
@Cacheable(value = "emp",key = "#p0"),
...
},
put = {
@CachePut(value = "emp",key = "#p0"),
...
},evict = {
@CacheEvict(value = "emp",key = "#p0"),
....
})
public User save(User user) {
....
}

整合 Caffeine

在 Spring Cache 中整合 Caffeine 可以通过以下步骤实现:

1. 添加依赖

在你的 pom.xml 中添加 Caffeine 依赖:

1
2
3
4
5
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version> <!-- 使用最新版本 -->
</dependency>

2. 配置 application.yml

application.yml 中配置 Spring Cache 和 Caffeine 的参数:

1
2
3
4
5
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=1000,expireAfterWrite=5m

这里的 spec 定义了 Caffeine 的缓存策略,可以指定:

  • maximumSize:缓存的最大条目数
  • expireAfterWrite:写入后到期时间(例如:5分钟)

3. 配置 CaffeineCacheManager(可选)

如果需要更精细的控制,可以创建一个配置类,用于配置 CaffeineCacheManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

@Configuration
@EnableCaching
public class CacheConfig {

@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(caffeineCacheBuilder());
return cacheManager;
}

@Bean
public Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(1000);
}
}

这个配置类定义了一个 CaffeineCacheManager 并设置了缓存策略,可以替代 application.yml 中的 spec 配置方式。

4. 使用 @Cacheable 注解

在需要缓存的方法上使用 @Cacheable 注解。例如:

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

@Cacheable(value = "users", key = "#userId")
public User getUserById(Long userId) {
// 假设这里是一个耗时的数据库查询
return findUserByIdFromDatabase(userId);
}
}

在这个例子中,当调用 getUserById 方法时,结果会被缓存到 users 缓存中,keyuserId。下一次调用相同参数时会直接从缓存中获取数据,而不是再次调用方法。

5. 常用注解

  • @Cacheable:用于标注需要缓存的方法。
  • @CachePut:每次调用时强制更新缓存。
  • @CacheEvict:清除指定缓存。

整合 Redis

在 Spring Cache 中整合 Redis,可以通过以下步骤完成:

1. 添加依赖

pom.xml 中添加 Spring Data Redis 依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2. 配置 application.yml

application.yml 中配置 Redis 的连接信息和 Spring Cache 的类型为 Redis:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring:
cache:
type: redis
redis:
time-to-live: 600000 # 默认缓存超时时间为10分钟(单位为毫秒)
cache-null-values: true # 是否缓存空值
key-prefix: "myapp::" # 设置 Redis 键的前缀
use-key-prefix: true # 启用键前缀
redis:
host: localhost # Redis 服务器地址
port: 6379 # Redis 服务器端口
password: yourpassword # Redis 密码(如果有的话)
lettuce:
pool:
max-active: 8 # 连接池最大连接数
max-idle: 8 # 连接池最大空闲连接数
min-idle: 0 # 连接池最小空闲连接数
timeout: 6000ms # 连接超时时间

3. 配置 RedisCacheManager(可选)

如果需要自定义 Redis 缓存配置,可以通过配置类自定义 RedisCacheManager,例如设置缓存的默认超时时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
@EnableCaching
public class RedisCacheConfig {

@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 设置缓存默认过期时间10分钟
.serializeKeysWith(RedisSerializationContext
.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));

return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
}
}

4. 使用 @Cacheable 注解

在需要缓存的方法上使用 @Cacheable 注解。例如:

1
2
3
4
5
6
7
8
9
10
11
12
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class UserService {

@Cacheable(value = "users", key = "#userId")
public User getUserById(Long userId) {
// 假设这是一个耗时的数据库查询
return findUserByIdFromDatabase(userId);
}
}

在这个例子中,当调用 getUserById 方法时,结果会被缓存到 Redis 的 users 缓存中,keyuserId。下一次调用相同参数时会直接从缓存中获取数据,而不是再次调用方法。

5. 常用注解

  • @Cacheable:用于标注需要缓存的方法。
  • @CachePut:每次调用时强制更新缓存。
  • @CacheEvict:清除指定缓存。

何时使用 Java 配置类?

​ 如果需要更复杂的配置,比如特定缓存的不同超时时间或自定义序列化器(如 JSON 序列化),则需要使用 Java 配置类。

注意事项

  • 缓存数据的过期时间:可以通过 RedisCacheConfigurationentryTtl 方法来设置默认的过期时间。
  • 序列化:在配置中,我们使用 GenericJackson2JsonRedisSerializer 作为值的序列化器,以支持对象序列化为 JSON。
  • Title: Spring Cache
  • Author: Lu
  • Created at : 2024-07-16 14:42:03
  • Updated at : 2024-07-16 16:45:03
  • Link: https://lusy.ink/2024/07/16/SpringCache/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments