1. 缓存概述

缓存是提高应用性能的重要手段,Spring Boot提供了完整的缓存解决方案。通过缓存可以减少数据库访问、提高响应速度、降低系统负载。

1.1 缓存类型

  • 本地缓存:JVM内存中的缓存,速度快但容量有限
  • 分布式缓存:Redis、Memcached等,支持集群部署
  • 数据库缓存:查询结果缓存、连接池缓存
  • HTTP缓存:浏览器缓存、CDN缓存

1.2 缓存策略

  • Cache-Aside:应用程序管理缓存
  • Read-Through:缓存自动从数据源读取
  • Write-Through:同时写入缓存和数据源
  • Write-Behind:异步写入数据源

1.3 核心依赖

<dependencies>
  <!-- Spring Boot Cache -->
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <!-- Spring Boot Data Redis -->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      <!-- Caffeine Cache -->
        <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        </dependency>
        <!-- EhCache -->
          <dependency>
          <groupId>org.ehcache</groupId>
          <artifactId>ehcache</artifactId>
          </dependency>
        </dependencies>

2. Spring Cache注解

2.1 基础缓存注解

package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class UserCacheService {
@Autowired
private UserRepository userRepository;
// 缓存查询结果
@Cacheable(value = "users", key = "#id")
public Optional<User> findById(Long id) {
  System.out.println("从数据库查询用户: " + id);
  return userRepository.findById(id);
  }
  // 缓存用户列表
  @Cacheable(value = "userList", key = "#status")
  public List<User> findByStatus(String status) {
    System.out.println("从数据库查询用户列表: " + status);
    return userRepository.findByStatus(status);
    }
    // 更新缓存
    @CachePut(value = "users", key = "#user.id")
    public User save(User user) {
    System.out.println("保存用户到数据库: " + user.getUsername());
    return userRepository.save(user);
    }
    // 清除缓存
    @CacheEvict(value = "users", key = "#id")
    public void deleteById(Long id) {
    System.out.println("从数据库删除用户: " + id);
    userRepository.deleteById(id);
    }
    // 清除所有用户缓存
    @CacheEvict(value = "users", allEntries = true)
    public void clearAllUserCache() {
    System.out.println("清除所有用户缓存");
    }
    // 组合缓存操作
    @Caching(
    evict = {
    @CacheEvict(value = "users", key = "#user.id"),
    @CacheEvict(value = "userList", allEntries = true)
    }
    )
    public User updateUser(User user) {
    System.out.println("更新用户: " + user.getUsername());
    return userRepository.save(user);
    }
    }

2.2 条件缓存

package com.example.demo.service;
import com.example.demo.entity.User;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
@Service
public class ConditionalCacheService {
// 条件缓存:只有活跃用户才缓存
@Cacheable(value = "activeUsers", condition = "#user.status == 'ACTIVE'")
public User cacheActiveUser(User user) {
System.out.println("缓存活跃用户: " + user.getUsername());
return user;
}
// 条件清除:只有特定状态才清除
@CacheEvict(value = "users", condition = "#user.status == 'INACTIVE'")
public void evictInactiveUser(User user) {
System.out.println("清除非活跃用户缓存: " + user.getUsername());
}
// 条件更新:只有特定条件才更新缓存
@CachePut(value = "users", unless = "#result == null")
public User updateUserConditionally(User user) {
if (user.getId() == null) {
return null; // 不缓存null结果
}
return user;
}
}

3. 缓存配置

3.1 基础缓存配置

package com.example.demo.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
cacheManager.setCacheNames("users", "userList", "activeUsers");
return cacheManager;
}
}

3.2 Caffeine缓存配置

package com.example.demo.config;
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 CaffeineCacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.expireAfterAccess(5, TimeUnit.MINUTES)
.recordStats());
return cacheManager;
}
}

3.3 Redis缓存配置

package com.example.demo.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
public class RedisCacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 配置序列化
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
  ObjectMapper mapper = new ObjectMapper();
  mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
  serializer.setObjectMapper(mapper);
  // 配置缓存
  RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
  .entryTtl(Duration.ofMinutes(10))
  .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
  .disableCachingNullValues();
  return RedisCacheManager.builder(connectionFactory)
  .cacheDefaults(config)
  .build();
  }
  }

4. 多级缓存

4.1 多级缓存实现

package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
@Service
public class MultiLevelCacheService {
@Autowired
private UserRepository userRepository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
  private static final String REDIS_KEY_PREFIX = "user:";
  private static final long REDIS_EXPIRE_TIME = 30; // 30分钟
  // L1缓存:本地缓存
  @Cacheable(value = "localUsers", key = "#id")
  public Optional<User> getUserFromL1Cache(Long id) {
    System.out.println("L1缓存未命中,查询L2缓存");
    return getUserFromL2Cache(id);
    }
    // L2缓存:Redis缓存
    public Optional<User> getUserFromL2Cache(Long id) {
      String redisKey = REDIS_KEY_PREFIX + id;
      User user = (User) redisTemplate.opsForValue().get(redisKey);
      if (user != null) {
      System.out.println("L2缓存命中: " + id);
      return Optional.of(user);
      }
      System.out.println("L2缓存未命中,查询数据库");
      return getUserFromDatabase(id);
      }
      // L3缓存:数据库
      public Optional<User> getUserFromDatabase(Long id) {
        System.out.println("从数据库查询用户: " + id);
        Optional<User> user = userRepository.findById(id);
          if (user.isPresent()) {
          // 写入L2缓存
          String redisKey = REDIS_KEY_PREFIX + id;
          redisTemplate.opsForValue().set(redisKey, user.get(), REDIS_EXPIRE_TIME, TimeUnit.MINUTES);
          }
          return user;
          }
          }

4.2 缓存预热

package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;
@Service
public class CacheWarmupService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserCacheService userCacheService;
@PostConstruct
public void warmupCache() {
System.out.println("开始缓存预热...");
// 预热活跃用户缓存
List<User> activeUsers = userRepository.findByStatus("ACTIVE");
  for (User user : activeUsers) {
  userCacheService.findById(user.getId());
  }
  System.out.println("缓存预热完成,预热了 " + activeUsers.size() + " 个用户");
  }
  }

5. 缓存监控

5.1 缓存统计

package com.example.demo.service;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class CacheStatsService {
@Autowired
private CacheManager cacheManager;
public Map<String, Object> getCacheStats() {
  Map<String, Object> stats = new HashMap<>();
    cacheManager.getCacheNames().forEach(cacheName -> {
    CaffeineCache caffeineCache = (CaffeineCache) cacheManager.getCache(cacheName);
    if (caffeineCache != null) {
    Cache<Object, Object> nativeCache = caffeineCache.getNativeCache();
      CacheStats cacheStats = nativeCache.stats();
      Map<String, Object> cacheStatsMap = new HashMap<>();
        cacheStatsMap.put("hitCount", cacheStats.hitCount());
        cacheStatsMap.put("missCount", cacheStats.missCount());
        cacheStatsMap.put("hitRate", cacheStats.hitRate());
        cacheStatsMap.put("evictionCount", cacheStats.evictionCount());
        cacheStatsMap.put("size", nativeCache.estimatedSize());
        stats.put(cacheName, cacheStatsMap);
        }
        });
        return stats;
        }
        }

5.2 缓存监控端点

package com.example.demo.controller;
import com.example.demo.service.CacheStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/api/cache")
public class CacheController {
@Autowired
private CacheStatsService cacheStatsService;
@GetMapping("/stats")
public Map<String, Object> getCacheStats() {
  return cacheStatsService.getCacheStats();
  }
  }

6. 缓存策略

6.1 缓存更新策略

package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class CacheUpdateStrategyService {
@Autowired
private UserRepository userRepository;
// 写回策略:同时更新缓存和数据库
@CachePut(value = "users", key = "#user.id")
@Transactional
public User writeThrough(User user) {
System.out.println("写回策略:同时更新缓存和数据库");
return userRepository.save(user);
}
// 写分配策略:先更新数据库,再更新缓存
@Transactional
public User writeAllocate(User user) {
System.out.println("写分配策略:先更新数据库");
User savedUser = userRepository.save(user);
// 手动更新缓存
updateCache(savedUser);
return savedUser;
}
// 写不分配策略:只更新数据库,清除缓存
@CacheEvict(value = "users", key = "#user.id")
@Transactional
public User writeNoAllocate(User user) {
System.out.println("写不分配策略:只更新数据库,清除缓存");
return userRepository.save(user);
}
// 异步写回策略
@Transactional
public User writeBehind(User user) {
System.out.println("异步写回策略:先更新缓存,异步更新数据库");
updateCache(user);
// 异步更新数据库
asyncUpdateDatabase(user);
return user;
}
private void updateCache(User user) {
// 实现缓存更新逻辑
}
private void asyncUpdateDatabase(User user) {
// 实现异步数据库更新逻辑
}
}

6.2 缓存一致性

package com.example.demo.service;
import com.example.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class CacheConsistencyService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
  // 强一致性:使用分布式锁
  public User updateUserWithLock(User user) {
  String lockKey = "lock:user:" + user.getId();
  String lockValue = String.valueOf(System.currentTimeMillis());
  try {
  // 获取分布式锁
  Boolean lockAcquired = redisTemplate.opsForValue()
  .setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);
  if (lockAcquired) {
  // 更新数据库
  updateDatabase(user);
  // 更新缓存
  updateCache(user);
  return user;
  } else {
  throw new RuntimeException("获取锁失败");
  }
  } finally {
  // 释放锁
  releaseLock(lockKey, lockValue);
  }
  }
  // 最终一致性:使用消息队列
  public User updateUserWithMQ(User user) {
  // 更新数据库
  updateDatabase(user);
  // 发送缓存更新消息
  sendCacheUpdateMessage(user);
  return user;
  }
  private void updateDatabase(User user) {
  // 实现数据库更新逻辑
  }
  private void updateCache(User user) {
  // 实现缓存更新逻辑
  }
  private void sendCacheUpdateMessage(User user) {
  // 实现消息发送逻辑
  }
  private void releaseLock(String lockKey, String lockValue) {
  // 实现锁释放逻辑
  }
  }

7. 缓存性能优化

7.1 缓存预热

package com.example.demo.service;
import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Service
public class CacheWarmupService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserCacheService userCacheService;
// 异步预热缓存
@Async
public CompletableFuture<Void> warmupCacheAsync() {
  System.out.println("开始异步缓存预热...");
  List<User> users = userRepository.findAll();
    for (User user : users) {
    userCacheService.findById(user.getId());
    }
    System.out.println("异步缓存预热完成");
    return CompletableFuture.completedFuture(null);
    }
    // 分批预热缓存
    public void warmupCacheInBatches(int batchSize) {
    System.out.println("开始分批缓存预热...");
    List<User> users = userRepository.findAll();
      for (int i = 0; i < users.size(); i += batchSize) {
      int endIndex = Math.min(i + batchSize, users.size());
      List<User> batch = users.subList(i, endIndex);
        for (User user : batch) {
        userCacheService.findById(user.getId());
        }
        System.out.println("预热批次 " + (i / batchSize + 1) + " 完成");
        }
        System.out.println("分批缓存预热完成");
        }
        }

7.2 缓存压缩

package com.example.demo.service;
import com.example.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@Service
public class CacheCompressionService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
  // 压缩存储
  public void storeCompressed(String key, User user) {
  try {
  byte[] compressed = compress(user);
  redisTemplate.opsForValue().set(key, compressed);
  } catch (Exception e) {
  throw new RuntimeException("压缩存储失败", e);
  }
  }
  // 解压读取
  public User getCompressed(String key) {
  try {
  byte[] compressed = (byte[]) redisTemplate.opsForValue().get(key);
  if (compressed != null) {
  return decompress(compressed);
  }
  return null;
  } catch (Exception e) {
  throw new RuntimeException("解压读取失败", e);
  }
  }
  private byte[] compress(User user) throws Exception {
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  GZIPOutputStream gzos = new GZIPOutputStream(baos);
  ObjectOutputStream oos = new ObjectOutputStream(gzos);
  oos.writeObject(user);
  oos.close();
  return baos.toByteArray();
  }
  private User decompress(byte[] compressed) throws Exception {
  ByteArrayInputStream bais = new ByteArrayInputStream(compressed);
  GZIPInputStream gzis = new GZIPInputStream(bais);
  ObjectInputStream ois = new ObjectInputStream(gzis);
  User user = (User) ois.readObject();
  ois.close();
  return user;
  }
  }

8. 缓存配置优化

8.1 配置文件

# application.yml
spring:
cache:
type: caffeine
caffeine:
spec: maximumSize=1000,expireAfterWrite=10m,expireAfterAccess=5m
redis:
time-to-live: 600000 # 10分钟
cache-null-values: false
use-key-prefix: true
key-prefix: "cache:"
redis:
host: localhost
port: 6379
password:
database: 0
timeout: 2000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
# 缓存监控
management:
endpoints:
web:
exposure:
include: cache,health,metrics
endpoint:
cache:
enabled: true
metrics:
export:
prometheus:
enabled: true

8.2 高级缓存配置

package com.example.demo.config;
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 org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableCaching
public class AdvancedCacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
// 配置不同的缓存策略
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
  // 用户缓存:10分钟过期
  cacheConfigurations.put("users", RedisCacheConfiguration.defaultCacheConfig()
  .entryTtl(Duration.ofMinutes(10))
  .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));
  // 用户列表缓存:5分钟过期
  cacheConfigurations.put("userList", RedisCacheConfiguration.defaultCacheConfig()
  .entryTtl(Duration.ofMinutes(5))
  .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));
  // 活跃用户缓存:30分钟过期
  cacheConfigurations.put("activeUsers", RedisCacheConfiguration.defaultCacheConfig()
  .entryTtl(Duration.ofMinutes(30))
  .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));
  return RedisCacheManager.builder(connectionFactory)
  .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
  .entryTtl(Duration.ofMinutes(10))
  .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
  .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())))
  .withInitialCacheConfigurations(cacheConfigurations)
  .build();
  }
  }

9. 总结

Spring Boot缓存机制提供了完整的缓存解决方案:

  1. 缓存注解:@Cacheable、@CachePut、@CacheEvict等
  2. 缓存配置:支持多种缓存实现(Caffeine、Redis、EhCache)
  3. 多级缓存:本地缓存+分布式缓存
  4. 缓存监控:缓存统计和性能监控
  5. 缓存策略:多种缓存更新策略
  6. 性能优化:缓存预热、压缩、异步处理
  7. 配置优化:灵活的缓存配置

通过合理使用这些缓存特性,可以显著提高应用性能和用户体验。