引言

在上一集中,我们深入了解了HTTP缓存机制。本集我们将继续探讨Spring Boot中的缓存框架集成,这是提升应用性能的另一个重要手段。Spring Boot提供了统一的缓存抽象,支持多种缓存实现,包括Redis、EhCache、Caffeine等。通过合理配置和使用缓存框架,可以显著提升应用的响应速度和并发处理能力。

Spring Boot缓存抽象

什么是Spring Cache

Spring Cache是Spring框架提供的缓存抽象,它提供了一种声明式的缓存管理方式。通过注解的方式,开发者可以轻松地在方法上添加缓存功能,而无需关心底层的缓存实现细节。

Spring Cache的核心特性

  • 声明式缓存:通过注解方式管理缓存
  • 多种实现支持:支持Redis、EhCache、Caffeine等多种缓存实现
  • 统一抽象:提供统一的缓存操作接口
  • 自动配置:Spring Boot提供自动配置支持
  • 灵活配置:支持多种缓存策略和配置选项

启用Spring Cache

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

public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}

// 缓存配置
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(
new ConcurrentMapCache("users"),
new ConcurrentMapCache("products"),
new ConcurrentMapCache("orders")
));
return cacheManager;
}
}

Spring Cache注解详解

1. @Cacheable注解

@Cacheable注解用于标注将方法的返回结果缓存,适用于查询操作:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Service
public class UserService {

@Autowired
private UserRepository userRepository;

// 基本缓存使用
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
System.out.println("从数据库查询用户: " + id);
return userRepository.findById(id).orElse(null);
}

// 条件缓存
@Cacheable(value = "users", key = "#id", condition = "#id > 0")
public User getUserByIdWithCondition(Long id) {
System.out.println("条件缓存查询用户: " + id);
return userRepository.findById(id).orElse(null);
}

// 排除缓存
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUserByIdUnless(Long id) {
System.out.println("排除缓存查询用户: " + id);
return userRepository.findById(id).orElse(null);
}

// 复合键缓存
@Cacheable(value = "users", key = "#name + '_' + #email")
public User getUserByNameAndEmail(String name, String email) {
System.out.println("复合键缓存查询用户: " + name + ", " + email);
return userRepository.findByNameAndEmail(name, email);
}

// 自定义键生成器
@Cacheable(value = "users", keyGenerator = "customKeyGenerator")
public User getUserByCustomKey(Long id) {
System.out.println("自定义键生成器查询用户: " + id);
return userRepository.findById(id).orElse(null);
}
}

2. @CachePut注解

@CachePut注解用于更新缓存,方法每次都会执行,并将结果更新到缓存中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Service
public class UserService {

// 更新用户并缓存
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
System.out.println("更新用户: " + user.getId());
return userRepository.save(user);
}

// 创建用户并缓存
@CachePut(value = "users", key = "#result.id")
public User createUser(User user) {
System.out.println("创建用户: " + user.getName());
return userRepository.save(user);
}

// 条件更新缓存
@CachePut(value = "users", key = "#user.id", condition = "#user.active == true")
public User updateActiveUser(User user) {
System.out.println("更新活跃用户: " + user.getId());
return userRepository.save(user);
}
}

3. @CacheEvict注解

@CacheEvict注解用于清除缓存,常用于删除或更新操作后清除缓存:

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
28
29
30
@Service
public class UserService {

// 删除用户并清除缓存
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
System.out.println("删除用户: " + id);
userRepository.deleteById(id);
}

// 清除所有用户缓存
@CacheEvict(value = "users", allEntries = true)
public void clearAllUserCache() {
System.out.println("清除所有用户缓存");
}

// 条件清除缓存
@CacheEvict(value = "users", key = "#id", condition = "#id > 0")
public void deleteUserWithCondition(Long id) {
System.out.println("条件删除用户: " + id);
userRepository.deleteById(id);
}

// 清除缓存前执行
@CacheEvict(value = "users", key = "#id", beforeInvocation = true)
public void deleteUserBeforeInvocation(Long id) {
System.out.println("删除前清除缓存: " + id);
userRepository.deleteById(id);
}
}

4. @Caching注解

@Caching注解用于组合多个缓存操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class UserService {

// 组合多个缓存操作
@Caching(
cacheable = {
@Cacheable(value = "users", key = "#id"),
@Cacheable(value = "userProfiles", key = "#id")
},
put = {
@CachePut(value = "userStats", key = "#result.id")
},
evict = {
@CacheEvict(value = "userList", allEntries = true)
}
)
public User getUserWithMultipleCaches(Long id) {
System.out.println("多缓存操作查询用户: " + id);
return userRepository.findById(id).orElse(null);
}
}

5. @CacheConfig注解

@CacheConfig注解用于在类级别配置缓存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Service
@CacheConfig(cacheNames = "users", keyGenerator = "customKeyGenerator")
public class UserService {

// 使用类级别的缓存配置
@Cacheable
public User getUserById(Long id) {
System.out.println("使用类级别配置查询用户: " + id);
return userRepository.findById(id).orElse(null);
}

@CachePut(key = "#user.id")
public User updateUser(User user) {
System.out.println("使用类级别配置更新用户: " + user.getId());
return userRepository.save(user);
}

@CacheEvict
public void deleteUser(Long id) {
System.out.println("使用类级别配置删除用户: " + id);
userRepository.deleteById(id);
}
}

Redis缓存集成

1. Redis配置

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
@Configuration
@EnableCaching
public class RedisCacheConfig {

@Value("${spring.redis.host:localhost}")
private String redisHost;

@Value("${spring.redis.port:6379}")
private int redisPort;

@Value("${spring.redis.password:}")
private String redisPassword;

@Value("${spring.redis.database:0}")
private int redisDatabase;

// Redis连接工厂
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(redisHost);
config.setPort(redisPort);
config.setPassword(redisPassword);
config.setDatabase(redisDatabase);

return new LettuceConnectionFactory(config);
}

// Redis模板
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());

// 序列化配置
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

template.afterPropertiesSet();
return template;
}

// 缓存管理器
@Bean
public CacheManager cacheManager() {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));

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

// 自定义键生成器
@Bean
public KeyGenerator customKeyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(".");
sb.append(method.getName());
for (Object param : params) {
sb.append(".");
sb.append(param.toString());
}
return sb.toString();
};
}
}

2. Redis缓存服务

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Service
public class RedisCacheService {

@Autowired
private RedisTemplate<String, Object> redisTemplate;

// 设置缓存
public void setCache(String key, Object value, long timeout) {
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
}

// 获取缓存
public Object getCache(String key) {
return redisTemplate.opsForValue().get(key);
}

// 删除缓存
public void deleteCache(String key) {
redisTemplate.delete(key);
}

// 批量删除缓存
public void deleteCacheByPattern(String pattern) {
Set<String> keys = redisTemplate.keys(pattern);
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}

// 检查缓存是否存在
public boolean hasCache(String key) {
return redisTemplate.hasKey(key);
}

// 设置过期时间
public void expireCache(String key, long timeout) {
redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
}

// 获取剩余过期时间
public long getCacheExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
}

3. Redis缓存应用示例

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
@RestController
@RequestMapping("/api/users")
public class UserController {

@Autowired
private UserService userService;

@Autowired
private RedisCacheService redisCacheService;

// 获取用户信息(使用Spring Cache)
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
if (user != null) {
return ResponseEntity.ok(user);
}
return ResponseEntity.notFound().build();
}

// 更新用户信息
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
User updatedUser = userService.updateUser(user);
return ResponseEntity.ok(updatedUser);
}

// 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.ok().build();
}

// 手动缓存操作
@GetMapping("/cache/{id}")
public ResponseEntity<User> getUserWithManualCache(@PathVariable Long id) {
String cacheKey = "user:" + id;

// 先尝试从缓存获取
User user = (User) redisCacheService.getCache(cacheKey);
if (user != null) {
System.out.println("从Redis缓存获取用户: " + id);
return ResponseEntity.ok(user);
}

// 缓存未命中,从数据库获取
user = userService.getUserById(id);
if (user != null) {
// 存入缓存,设置5分钟过期
redisCacheService.setCache(cacheKey, user, 300);
System.out.println("用户信息已缓存: " + id);
}

return ResponseEntity.ok(user);
}

// 清除用户缓存
@PostMapping("/cache/clear/{id}")
public ResponseEntity<String> clearUserCache(@PathVariable Long id) {
String cacheKey = "user:" + id;
redisCacheService.deleteCache(cacheKey);
return ResponseEntity.ok("用户缓存已清除: " + id);
}

// 批量清除用户缓存
@PostMapping("/cache/clear-all")
public ResponseEntity<String> clearAllUserCache() {
redisCacheService.deleteCacheByPattern("user:*");
return ResponseEntity.ok("所有用户缓存已清除");
}
}

EhCache缓存集成

1. EhCache配置

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

// EhCache管理器
@Bean
public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();
factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
factoryBean.setShared(true);
return factoryBean;
}

// 缓存管理器
@Bean
public CacheManager cacheManager() {
EhCacheCacheManager cacheManager = new EhCacheCacheManager();
cacheManager.setCacheManager(ehCacheManagerFactoryBean().getObject());
return cacheManager;
}
}

2. EhCache配置文件

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<!-- ehcache.xml -->
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

<!-- 用户缓存配置 -->
<cache name="users"
maxEntriesLocalHeap="1000"
maxEntriesLocalDisk="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
diskSpoolBufferSizeMB="20"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</cache>

<!-- 产品缓存配置 -->
<cache name="products"
maxEntriesLocalHeap="500"
maxEntriesLocalDisk="5000"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="1200"
diskSpoolBufferSizeMB="20"
memoryStoreEvictionPolicy="LFU">
<persistence strategy="localTempSwap"/>
</cache>

<!-- 订单缓存配置 -->
<cache name="orders"
maxEntriesLocalHeap="200"
maxEntriesLocalDisk="2000"
eternal="false"
timeToIdleSeconds="180"
timeToLiveSeconds="360"
diskSpoolBufferSizeMB="20"
memoryStoreEvictionPolicy="FIFO">
<persistence strategy="localTempSwap"/>
</cache>

<!-- 默认缓存配置 -->
<defaultCache
maxEntriesLocalHeap="100"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxEntriesLocalDisk="1000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
</ehcache>

3. EhCache缓存服务

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@Service
public class EhCacheService {

@Autowired
private CacheManager cacheManager;

// 获取缓存
public Object getCache(String cacheName, String key) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
Cache.ValueWrapper wrapper = cache.get(key);
return wrapper != null ? wrapper.get() : null;
}
return null;
}

// 设置缓存
public void setCache(String cacheName, String key, Object value) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.put(key, value);
}
}

// 删除缓存
public void deleteCache(String cacheName, String key) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.evict(key);
}
}

// 清除所有缓存
public void clearAllCache(String cacheName) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
}
}

// 获取缓存统计信息
public CacheStatistics getCacheStatistics(String cacheName) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
EhCache ehCache = (EhCache) cache.getNativeCache();
Statistics stats = ehCache.getStatistics();

CacheStatistics statistics = new CacheStatistics();
statistics.setCacheName(cacheName);
statistics.setHitCount(stats.getCacheHits());
statistics.setMissCount(stats.getCacheMisses());
statistics.setHitRate(stats.getCacheHitPercentage());
statistics.setSize(stats.getObjectCount());

return statistics;
}
return null;
}

// 缓存统计信息类
public static class CacheStatistics {
private String cacheName;
private long hitCount;
private long missCount;
private double hitRate;
private long size;

// getters and setters
public String getCacheName() { return cacheName; }
public void setCacheName(String cacheName) { this.cacheName = cacheName; }

public long getHitCount() { return hitCount; }
public void setHitCount(long hitCount) { this.hitCount = hitCount; }

public long getMissCount() { return missCount; }
public void setMissCount(long missCount) { this.missCount = missCount; }

public double getHitRate() { return hitRate; }
public void setHitRate(double hitRate) { this.hitRate = hitRate; }

public long getSize() { return size; }
public void setSize(long size) { this.size = size; }
}
}

Caffeine缓存集成

1. Caffeine配置

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@Configuration
@EnableCaching
public class CaffeineCacheConfig {

// Caffeine缓存管理器
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();

// 用户缓存配置
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.expireAfterAccess(5, TimeUnit.MINUTES)
.recordStats());

// 产品缓存配置
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(500)
.expireAfterWrite(30, TimeUnit.MINUTES)
.expireAfterAccess(15, TimeUnit.MINUTES)
.recordStats());

// 订单缓存配置
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(200)
.expireAfterWrite(5, TimeUnit.MINUTES)
.expireAfterAccess(2, TimeUnit.MINUTES)
.recordStats());

return cacheManager;
}

// 自定义缓存配置
@Bean
public CacheManager customCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();

// 配置不同的缓存策略
Map<String, Caffeine<Object, Object>> cacheConfigs = new HashMap<>();

// 用户缓存:大容量,长时间过期
cacheConfigs.put("users", Caffeine.newBuilder()
.maximumSize(2000)
.expireAfterWrite(1, TimeUnit.HOURS)
.expireAfterAccess(30, TimeUnit.MINUTES)
.recordStats());

// 产品缓存:中等容量,中等过期时间
cacheConfigs.put("products", Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(30, TimeUnit.MINUTES)
.expireAfterAccess(15, TimeUnit.MINUTES)
.recordStats());

// 订单缓存:小容量,短过期时间
cacheConfigs.put("orders", Caffeine.newBuilder()
.maximumSize(500)
.expireAfterWrite(10, TimeUnit.MINUTES)
.expireAfterAccess(5, TimeUnit.MINUTES)
.recordStats());

cacheManager.setCaffeine(cacheConfigs);
return cacheManager;
}
}

2. Caffeine缓存服务

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
@Service
public class CaffeineCacheService {

@Autowired
private CacheManager cacheManager;

// 获取缓存
public Object getCache(String cacheName, String key) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
Cache.ValueWrapper wrapper = cache.get(key);
return wrapper != null ? wrapper.get() : null;
}
return null;
}

// 设置缓存
public void setCache(String cacheName, String key, Object value) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.put(key, value);
}
}

// 删除缓存
public void deleteCache(String cacheName, String key) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.evict(key);
}
}

// 清除所有缓存
public void clearAllCache(String cacheName) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
}
}

// 获取缓存统计信息
public CaffeineCacheStatistics getCacheStatistics(String cacheName) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
com.github.benmanes.caffeine.cache.Cache<Object, Object> caffeineCache =
(com.github.benmanes.caffeine.cache.Cache<Object, Object>) cache.getNativeCache();

CaffeineCacheStatistics statistics = new CaffeineCacheStatistics();
statistics.setCacheName(cacheName);
statistics.setHitCount(caffeineCache.stats().hitCount());
statistics.setMissCount(caffeineCache.stats().missCount());
statistics.setHitRate(caffeineCache.stats().hitRate());
statistics.setSize(caffeineCache.estimatedSize());
statistics.setEvictionCount(caffeineCache.stats().evictionCount());

return statistics;
}
return null;
}

// Caffeine缓存统计信息类
public static class CaffeineCacheStatistics {
private String cacheName;
private long hitCount;
private long missCount;
private double hitRate;
private long size;
private long evictionCount;

// getters and setters
public String getCacheName() { return cacheName; }
public void setCacheName(String cacheName) { this.cacheName = cacheName; }

public long getHitCount() { return hitCount; }
public void setHitCount(long hitCount) { this.hitCount = hitCount; }

public long getMissCount() { return missCount; }
public void setMissCount(long missCount) { this.missCount = missCount; }

public double getHitRate() { return hitRate; }
public void setHitRate(double hitRate) { this.hitRate = hitRate; }

public long getSize() { return size; }
public void setSize(long size) { this.size = size; }

public long getEvictionCount() { return evictionCount; }
public void setEvictionCount(long evictionCount) { this.evictionCount = evictionCount; }
}
}

缓存性能优化

1. 缓存预热

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
@Component
public class CacheWarmupService {

@Autowired
private UserService userService;

@Autowired
private ProductService productService;

@Autowired
private OrderService orderService;

// 应用启动后预热缓存
@EventListener(ApplicationReadyEvent.class)
public void warmupCache() {
System.out.println("开始预热缓存...");

// 预热用户缓存
warmupUserCache();

// 预热产品缓存
warmupProductCache();

// 预热订单缓存
warmupOrderCache();

System.out.println("缓存预热完成");
}

// 预热用户缓存
private void warmupUserCache() {
System.out.println("预热用户缓存...");

// 获取热门用户ID列表
List<Long> popularUserIds = getPopularUserIds();

for (Long userId : popularUserIds) {
try {
userService.getUserById(userId);
System.out.println("用户缓存预热: " + userId);
} catch (Exception e) {
System.err.println("用户缓存预热失败: " + userId + ", " + e.getMessage());
}
}
}

// 预热产品缓存
private void warmupProductCache() {
System.out.println("预热产品缓存...");

// 获取热门产品ID列表
List<Long> popularProductIds = getPopularProductIds();

for (Long productId : popularProductIds) {
try {
productService.getProductById(productId);
System.out.println("产品缓存预热: " + productId);
} catch (Exception e) {
System.err.println("产品缓存预热失败: " + productId + ", " + e.getMessage());
}
}
}

// 预热订单缓存
private void warmupOrderCache() {
System.out.println("预热订单缓存...");

// 获取最近订单ID列表
List<Long> recentOrderIds = getRecentOrderIds();

for (Long orderId : recentOrderIds) {
try {
orderService.getOrderById(orderId);
System.out.println("订单缓存预热: " + orderId);
} catch (Exception e) {
System.err.println("订单缓存预热失败: " + orderId + ", " + e.getMessage());
}
}
}

// 获取热门用户ID列表
private List<Long> getPopularUserIds() {
// 模拟获取热门用户ID
return Arrays.asList(1L, 2L, 3L, 4L, 5L);
}

// 获取热门产品ID列表
private List<Long> getPopularProductIds() {
// 模拟获取热门产品ID
return Arrays.asList(1L, 2L, 3L, 4L, 5L);
}

// 获取最近订单ID列表
private List<Long> getRecentOrderIds() {
// 模拟获取最近订单ID
return Arrays.asList(1L, 2L, 3L, 4L, 5L);
}
}

2. 缓存监控

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
@RestController
@RequestMapping("/api/cache-monitor")
public class CacheMonitorController {

@Autowired
private EhCacheService ehCacheService;

@Autowired
private CaffeineCacheService caffeineCacheService;

@Autowired
private RedisCacheService redisCacheService;

// 获取缓存统计信息
@GetMapping("/stats")
public ResponseEntity<Map<String, Object>> getCacheStats() {
Map<String, Object> stats = new HashMap<>();

// EhCache统计信息
Map<String, EhCacheService.CacheStatistics> ehCacheStats = new HashMap<>();
ehCacheStats.put("users", ehCacheService.getCacheStatistics("users"));
ehCacheStats.put("products", ehCacheService.getCacheStatistics("products"));
ehCacheStats.put("orders", ehCacheService.getCacheStatistics("orders"));
stats.put("ehCache", ehCacheStats);

// Caffeine统计信息
Map<String, CaffeineCacheService.CaffeineCacheStatistics> caffeineStats = new HashMap<>();
caffeineStats.put("users", caffeineCacheService.getCacheStatistics("users"));
caffeineStats.put("products", caffeineCacheService.getCacheStatistics("products"));
caffeineStats.put("orders", caffeineCacheService.getCacheStatistics("orders"));
stats.put("caffeine", caffeineStats);

// Redis统计信息
Map<String, Object> redisStats = new HashMap<>();
redisStats.put("connected", redisCacheService.hasCache("test"));
redisStats.put("memory", getRedisMemoryUsage());
stats.put("redis", redisStats);

return ResponseEntity.ok(stats);
}

// 清除指定缓存
@PostMapping("/clear/{cacheName}")
public ResponseEntity<String> clearCache(@PathVariable String cacheName) {
try {
// 清除EhCache
ehCacheService.clearAllCache(cacheName);

// 清除Caffeine
caffeineCacheService.clearAllCache(cacheName);

// 清除Redis
redisCacheService.deleteCacheByPattern(cacheName + ":*");

return ResponseEntity.ok("缓存清除成功: " + cacheName);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("缓存清除失败: " + e.getMessage());
}
}

// 清除所有缓存
@PostMapping("/clear-all")
public ResponseEntity<String> clearAllCache() {
try {
// 清除所有EhCache
ehCacheService.clearAllCache("users");
ehCacheService.clearAllCache("products");
ehCacheService.clearAllCache("orders");

// 清除所有Caffeine
caffeineCacheService.clearAllCache("users");
caffeineCacheService.clearAllCache("products");
caffeineCacheService.clearAllCache("orders");

// 清除所有Redis
redisCacheService.deleteCacheByPattern("*");

return ResponseEntity.ok("所有缓存清除成功");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("缓存清除失败: " + e.getMessage());
}
}

// 获取Redis内存使用情况
private Map<String, Object> getRedisMemoryUsage() {
Map<String, Object> memoryInfo = new HashMap<>();
// 这里可以添加获取Redis内存使用情况的逻辑
memoryInfo.put("used", "100MB");
memoryInfo.put("peak", "150MB");
memoryInfo.put("fragmentation", "1.2");
return memoryInfo;
}
}

实际项目应用案例

1. 电商系统缓存集成

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
@Service
public class EcommerceCacheService {

@Autowired
private UserService userService;

@Autowired
private ProductService productService;

@Autowired
private OrderService orderService;

// 商品详情缓存
@Cacheable(value = "products", key = "#productId", unless = "#result == null")
public Product getProductById(Long productId) {
System.out.println("从数据库查询商品: " + productId);
return productService.findById(productId);
}

// 商品列表缓存
@Cacheable(value = "productList", key = "#category + '_' + #page + '_' + #size")
public List<Product> getProductsByCategory(String category, int page, int size) {
System.out.println("从数据库查询商品列表: " + category + ", " + page + ", " + size);
return productService.findByCategory(category, page, size);
}

// 用户购物车缓存
@Cacheable(value = "userCart", key = "#userId")
public Cart getUserCart(Long userId) {
System.out.println("从数据库查询用户购物车: " + userId);
return userService.getCartByUserId(userId);
}

// 更新购物车并缓存
@CachePut(value = "userCart", key = "#userId")
public Cart updateUserCart(Long userId, Cart cart) {
System.out.println("更新用户购物车: " + userId);
return userService.updateCart(userId, cart);
}

// 清空购物车并清除缓存
@CacheEvict(value = "userCart", key = "#userId")
public void clearUserCart(Long userId) {
System.out.println("清空用户购物车: " + userId);
userService.clearCart(userId);
}

// 订单缓存
@Cacheable(value = "orders", key = "#orderId")
public Order getOrderById(Long orderId) {
System.out.println("从数据库查询订单: " + orderId);
return orderService.findById(orderId);
}

// 创建订单并缓存
@CachePut(value = "orders", key = "#result.id")
public Order createOrder(Order order) {
System.out.println("创建订单: " + order.getId());
return orderService.save(order);
}

// 取消订单并清除缓存
@CacheEvict(value = "orders", key = "#orderId")
public void cancelOrder(Long orderId) {
System.out.println("取消订单: " + orderId);
orderService.cancelOrder(orderId);
}
}

2. 内容管理系统缓存集成

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@Service
public class CmsCacheService {

@Autowired
private ArticleService articleService;

@Autowired
private CategoryService categoryService;

@Autowired
private TagService tagService;

// 文章内容缓存
@Cacheable(value = "articles", key = "#articleId")
public Article getArticleById(Long articleId) {
System.out.println("从数据库查询文章: " + articleId);
return articleService.findById(articleId);
}

// 文章列表缓存
@Cacheable(value = "articleList", key = "#category + '_' + #page + '_' + #size")
public List<Article> getArticlesByCategory(String category, int page, int size) {
System.out.println("从数据库查询文章列表: " + category + ", " + page + ", " + size);
return articleService.findByCategory(category, page, size);
}

// 热门文章缓存
@Cacheable(value = "popularArticles", key = "#limit")
public List<Article> getPopularArticles(int limit) {
System.out.println("从数据库查询热门文章: " + limit);
return articleService.findPopularArticles(limit);
}

// 更新文章并缓存
@CachePut(value = "articles", key = "#article.id")
public Article updateArticle(Article article) {
System.out.println("更新文章: " + article.getId());
return articleService.save(article);
}

// 删除文章并清除缓存
@CacheEvict(value = "articles", key = "#articleId")
public void deleteArticle(Long articleId) {
System.out.println("删除文章: " + articleId);
articleService.deleteById(articleId);
}

// 分类缓存
@Cacheable(value = "categories")
public List<Category> getAllCategories() {
System.out.println("从数据库查询所有分类");
return categoryService.findAll();
}

// 标签缓存
@Cacheable(value = "tags")
public List<Tag> getAllTags() {
System.out.println("从数据库查询所有标签");
return tagService.findAll();
}
}

总结

通过本文的详细介绍和代码实操,我们深入了解了Spring Boot中的缓存框架集成:

核心要点

  1. Spring Cache抽象:提供统一的缓存操作接口
  2. 多种缓存实现:支持Redis、EhCache、Caffeine等
  3. 声明式缓存:通过注解方式管理缓存
  4. 灵活配置:支持多种缓存策略和配置选项
  5. 性能优化:缓存预热、监控、统计等功能

最佳实践

  • 合理选择缓存实现:根据业务需求选择合适的缓存框架
  • 优化缓存策略:设置合适的过期时间和容量限制
  • 实现缓存预热:应用启动时预热常用数据
  • 监控缓存性能:持续监控缓存命中率和性能指标
  • 处理缓存一致性:确保缓存与数据库数据的一致性

注意事项

  • 缓存键的设计要合理,避免冲突
  • 注意缓存的内存使用,避免内存溢出
  • 实现缓存降级策略,确保系统稳定性
  • 定期清理过期缓存,释放内存空间
  • 监控缓存性能,及时调整缓存策略

掌握Spring Boot缓存框架的集成和使用,将大大提升应用的性能和用户体验。