此文是 【Spring 容器详解】-> 【ApplicationContext 做了哪些企业化的增强?】的支节点。
在Spring框架中,ApplicationContext是BeanFactory的重要扩展,提供了更丰富的功能。其中,缓存支持(Caching)是ApplicationContext相对于BeanFactory的一个重要扩展功能。
1. BeanFactory的缓存支持限制
1.1 BeanFactory的局限性
BeanFactory作为Spring的核心容器,主要专注于Bean的生命周期管理,在缓存支持方面存在以下限制:
- 无内置缓存支持:BeanFactory本身不提供缓存功能
- 缺乏缓存抽象:无法统一管理不同类型的缓存
- 无缓存注解支持:不支持@Cacheable等缓存注解
- 缓存配置复杂:需要手动配置缓存管理器
1.2 示例代码
java
// BeanFactory缺乏缓存支持
public class BeanFactoryCachingExample {
public static void main(String[] args) {
// 创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// BeanFactory本身不提供缓存功能
// 无法使用@Cacheable注解
// 无法自动配置缓存管理器
// 需要通过其他方式实现缓存
// 手动实现缓存
Map<String, Object> cache = new ConcurrentHashMap<>();
// 手动缓存操作
String key = "user:1";
Object value = cache.get(key);
if (value == null) {
value = loadUserFromDatabase(1);
cache.put(key, value);
}
}
private static Object loadUserFromDatabase(int id) {
// 模拟从数据库加载数据
return "User " + id;
}
}
2. ApplicationContext的缓存支持扩展
2.1 核心接口:CacheManager
ApplicationContext通过CacheManager接口提供统一的缓存管理能力:
java
public interface CacheManager {
Cache getCache(String name);
Collection<String> getCacheNames();
}
2.2 缓存支持的核心功能
2.2.1 缓存注解支持
Spring提供了丰富的缓存注解来简化缓存操作:
java
@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);
}
// 更新缓存
@CachePut(value = "users", key = "#user.id")
public User createUser(User user) {
System.out.println("创建新用户: " + user.getName());
return userRepository.save(user);
}
// 清除缓存
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
System.out.println("删除用户: " + id);
userRepository.deleteById(id);
}
// 清除所有缓存
@CacheEvict(value = "users", allEntries = true)
public void clearAllUsers() {
System.out.println("清除所有用户缓存");
}
// 条件缓存
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUserByIdConditional(Long id) {
return userRepository.findById(id);
}
}
2.2.2 缓存管理器配置
java
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
// 使用ConcurrentMapCacheManager作为简单的内存缓存
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
cacheManager.setCacheNames(Arrays.asList("users", "products", "orders"));
return cacheManager;
}
// 使用Redis作为缓存
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
}
// 使用Caffeine作为本地缓存
@Bean
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES));
return cacheManager;
}
}
3. 缓存支持的具体实现
3.1 内置缓存实现
3.1.1 ConcurrentMapCache
java
@Configuration
public class ConcurrentMapCacheConfig {
@Bean
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
// 配置缓存名称
cacheManager.setCacheNames(Arrays.asList("users", "products"));
// 自定义缓存配置
cacheManager.setAllowNullValues(false);
return cacheManager;
}
}
@Component
public class ConcurrentMapCacheExample {
@Autowired
private CacheManager cacheManager;
public void demonstrateCache() {
Cache cache = cacheManager.getCache("users");
// 手动操作缓存
cache.put("key1", "value1");
Cache.ValueWrapper value = cache.get("key1");
System.out.println("缓存值: " + value.get());
// 检查缓存是否存在
if (cache.get("key2") == null) {
cache.put("key2", "value2");
}
}
}
3.1.2 Redis缓存
java
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName("localhost");
config.setPort(6379);
return new LettuceConnectionFactory(config);
}
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
// 配置不同的缓存策略
Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
// 用户缓存配置
cacheConfigurations.put("users", RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));
// 产品缓存配置
cacheConfigurations.put("products", RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(2))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig())
.withInitialCacheConfigurations(cacheConfigurations)
.build();
}
}
@Service
public class RedisCacheExample {
@Autowired
private CacheManager cacheManager;
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// 模拟从数据库加载
return new User(id, "User " + id);
}
@Cacheable(value = "products", key = "#id", unless = "#result == null")
public Product getProductById(Long id) {
// 模拟从数据库加载
return new Product(id, "Product " + id, 100.0);
}
}
3.1.3 Caffeine缓存
java
@Configuration
@EnableCaching
public class CaffeineCacheConfig {
@Bean
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
// 配置不同的缓存策略
Map<String, CaffeineCache> caches = new HashMap<>();
// 用户缓存:最大100个条目,10分钟过期
caches.put("users", new CaffeineCache("users", Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
.build()));
// 产品缓存:最大500个条目,1小时过期
caches.put("products", new CaffeineCache("products", Caffeine.newBuilder()
.maximumSize(500)
.expireAfterWrite(1, TimeUnit.HOURS)
.recordStats()
.build()));
cacheManager.setCaches(caches.values());
return cacheManager;
}
}
@Service
public class CaffeineCacheExample {
@Autowired
private CacheManager cacheManager;
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
// 模拟从数据库加载
return new User(id, "User " + id);
}
@Cacheable(value = "products", key = "#id")
public Product getProductById(Long id) {
// 模拟从数据库加载
return new Product(id, "Product " + id, 100.0);
}
// 获取缓存统计信息
public void printCacheStats() {
CaffeineCache usersCache = (CaffeineCache) cacheManager.getCache("users");
CaffeineCache productsCache = (CaffeineCache) cacheManager.getCache("products");
System.out.println("用户缓存统计: " + usersCache.getNativeCache().stats());
System.out.println("产品缓存统计: " + productsCache.getNativeCache().stats());
}
}
3.2 缓存注解详解
3.2.1 @Cacheable注解
java
@Service
public class CacheableExample {
@Autowired
private UserRepository userRepository;
// 基本用法
@Cacheable("users")
public User getUserById(Long id) {
return userRepository.findById(id);
}
// 指定key
@Cacheable(value = "users", key = "#id")
public User getUserByIdWithKey(Long id) {
return userRepository.findById(id);
}
// 复合key
@Cacheable(value = "users", key = "#user.id + '_' + #user.name")
public User getUserByUser(User user) {
return userRepository.findById(user.getId());
}
// 条件缓存
@Cacheable(value = "users", key = "#id", condition = "#id > 0")
public User getUserByIdConditional(Long id) {
return userRepository.findById(id);
}
// 除非条件
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUserByIdUnless(Long id) {
return userRepository.findById(id);
}
// 使用SpEL表达式
@Cacheable(value = "users", key = "T(java.lang.String).format('user:%d', #id)")
public User getUserByIdWithSpEL(Long id) {
return userRepository.findById(id);
}
}
3.2.2 @CachePut注解
java
@Service
public class CachePutExample {
@Autowired
private UserRepository userRepository;
// 更新缓存
@CachePut(value = "users", key = "#user.id")
public User createUser(User user) {
return userRepository.save(user);
}
// 条件更新
@CachePut(value = "users", key = "#user.id", condition = "#result != null")
public User updateUser(User user) {
return userRepository.save(user);
}
// 批量更新
@CachePut(value = "users", key = "#user.id")
public List<User> createUsers(List<User> users) {
return userRepository.saveAll(users);
}
}
3.2.3 @CacheEvict注解
java
@Service
public class CacheEvictExample {
@Autowired
private UserRepository userRepository;
// 删除指定key的缓存
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
// 删除所有缓存
@CacheEvict(value = "users", allEntries = true)
public void clearAllUsers() {
// 清除所有用户缓存
}
// 条件删除
@CacheEvict(value = "users", key = "#id", condition = "#result")
public boolean deleteUserConditional(Long id) {
return userRepository.deleteById(id);
}
// 删除多个缓存
@CacheEvict(value = {"users", "userDetails"}, key = "#id")
public void deleteUserAndDetails(Long id) {
userRepository.deleteById(id);
}
}
3.2.4 @Caching注解
java
@Service
public class CachingExample {
@Autowired
private UserRepository userRepository;
// 组合多个缓存操作
@Caching(
cacheable = {
@Cacheable(value = "users", key = "#id")
},
put = {
@CachePut(value = "userNames", key = "#result.name")
}
)
public User getUserById(Long id) {
return userRepository.findById(id);
}
// 复杂的缓存策略
@Caching(
evict = {
@CacheEvict(value = "users", key = "#user.id"),
@CacheEvict(value = "userNames", key = "#user.name")
},
put = {
@CachePut(value = "userEmails", key = "#user.email")
}
)
public User updateUser(User user) {
return userRepository.save(user);
}
}
4. 实际应用场景
4.1 数据库查询缓存
java
@Service
public class DatabaseCacheService {
@Autowired
private UserRepository userRepository;
@Autowired
private ProductRepository productRepository;
// 用户查询缓存
@Cacheable(value = "users", key = "#id")
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
@Cacheable(value = "users", key = "#email")
public User getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
// 产品查询缓存
@Cacheable(value = "products", key = "#id")
public Product getProductById(Long id) {
return productRepository.findById(id).orElse(null);
}
@Cacheable(value = "products", key = "'category:' + #categoryId")
public List<Product> getProductsByCategory(Long categoryId) {
return productRepository.findByCategoryId(categoryId);
}
// 更新操作
@CachePut(value = "users", key = "#user.id")
public User createUser(User user) {
return userRepository.save(user);
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
4.2 方法结果缓存
java
@Service
public class MethodResultCacheService {
// 计算密集型操作缓存
@Cacheable(value = "calculations", key = "#n")
public BigInteger calculateFactorial(int n) {
if (n <= 1) return BigInteger.ONE;
return BigInteger.valueOf(n).multiply(calculateFactorial(n - 1));
}
// 外部API调用缓存
@Cacheable(value = "weather", key = "#city", unless = "#result == null")
public WeatherInfo getWeatherInfo(String city) {
// 模拟外部API调用
return callWeatherAPI(city);
}
// 文件内容缓存
@Cacheable(value = "fileContent", key = "#filePath")
public String readFileContent(String filePath) {
try {
return Files.readString(Paths.get(filePath));
} catch (IOException e) {
throw new RuntimeException("读取文件失败", e);
}
}
private WeatherInfo callWeatherAPI(String city) {
// 模拟API调用
return new WeatherInfo(city, "晴天", 25.0);
}
}
4.3 缓存监控和管理
java
@Component
public class CacheMonitor {
@Autowired
private CacheManager cacheManager;
// 获取缓存统计信息
public Map<String, Object> getCacheStats() {
Map<String, Object> stats = new HashMap<>();
for (String cacheName : cacheManager.getCacheNames()) {
Cache cache = cacheManager.getCache(cacheName);
if (cache instanceof CaffeineCache) {
CaffeineCache caffeineCache = (CaffeineCache) cache;
stats.put(cacheName, caffeineCache.getNativeCache().stats());
}
}
return stats;
}
// 清除指定缓存
public void clearCache(String cacheName) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
}
}
// 清除所有缓存
public void clearAllCaches() {
for (String cacheName : cacheManager.getCacheNames()) {
clearCache(cacheName);
}
}
// 预热缓存
public void warmUpCache(String cacheName, List<Long> ids) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
for (Long id : ids) {
// 预热缓存
cache.get(id);
}
}
}
}
5. 高级特性
5.1 自定义缓存键生成器
java
@Component
public class CustomKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder key = new StringBuilder();
key.append(target.getClass().getSimpleName());
key.append(".");
key.append(method.getName());
key.append("(");
for (int i = 0; i < params.length; i++) {
if (i > 0) key.append(",");
key.append(params[i]);
}
key.append(")");
return key.toString();
}
}
@Configuration
@EnableCaching
public class CustomKeyGeneratorConfig {
@Bean
public KeyGenerator customKeyGenerator() {
return new CustomKeyGenerator();
}
}
@Service
public class CustomKeyGeneratorExample {
@Cacheable(value = "users", keyGenerator = "customKeyGenerator")
public User getUserById(Long id) {
// 使用自定义键生成器
return new User(id, "User " + id);
}
}
5.2 缓存条件表达式
java
@Service
public class ConditionalCacheExample {
@Autowired
private UserRepository userRepository;
// 基于参数的条件缓存
@Cacheable(value = "users", key = "#id", condition = "#id > 0")
public User getUserById(Long id) {
return userRepository.findById(id);
}
// 基于结果的条件缓存
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUserByIdUnlessNull(Long id) {
return userRepository.findById(id);
}
// 复杂的条件表达式
@Cacheable(value = "users", key = "#id",
condition = "#id > 0 and #id < 1000",
unless = "#result == null or #result.name == null")
public User getUserByIdComplex(Long id) {
return userRepository.findById(id);
}
// 基于时间的条件缓存
@Cacheable(value = "users", key = "#id",
condition = "T(java.time.LocalTime).now().getHour() < 22")
public User getUserByIdTimeBased(Long id) {
return userRepository.findById(id);
}
}
5.3 缓存同步
java
@Service
public class CacheSyncExample {
@Autowired
private UserRepository userRepository;
// 同步缓存操作
@Cacheable(value = "users", key = "#id", sync = true)
public User getUserByIdSync(Long id) {
// 防止缓存击穿
return userRepository.findById(id);
}
// 异步缓存更新
@Async
@CachePut(value = "users", key = "#user.id")
public CompletableFuture<User> updateUserAsync(User user) {
return CompletableFuture.completedFuture(userRepository.save(user));
}
}
6. 最佳实践
6.1 缓存策略设计
java
@Configuration
@EnableCaching
public class CacheStrategyConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
Map<String, CaffeineCache> caches = new HashMap<>();
// 热点数据缓存:小容量,短过期时间
caches.put("hotData", new CaffeineCache("hotData", Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(5, TimeUnit.MINUTES)
.recordStats()
.build()));
// 冷数据缓存:大容量,长过期时间
caches.put("coldData", new CaffeineCache("coldData", Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(1, TimeUnit.HOURS)
.recordStats()
.build()));
// 配置数据缓存:中等容量,中等过期时间
caches.put("configData", new CaffeineCache("configData", Caffeine.newBuilder()
.maximumSize(500)
.expireAfterWrite(30, TimeUnit.MINUTES)
.recordStats()
.build()));
cacheManager.setCaches(caches.values());
return cacheManager;
}
}
6.2 缓存异常处理
java
@Service
public class CacheExceptionHandlingExample {
@Autowired
private UserRepository userRepository;
// 缓存异常处理
@Cacheable(value = "users", key = "#id", unless = "#result == null")
public User getUserByIdWithExceptionHandling(Long id) {
try {
return userRepository.findById(id);
} catch (Exception e) {
// 记录异常但不影响缓存
System.err.println("获取用户失败: " + e.getMessage());
return null;
}
}
// 缓存回退机制
@Cacheable(value = "users", key = "#id")
public User getUserByIdWithFallback(Long id) {
try {
return userRepository.findById(id);
} catch (Exception e) {
// 返回默认值
return new User(id, "Default User");
}
}
}
6.3 缓存性能监控
6.3.1 缓存统计信息
java
@Component
public class CacheMonitor {
@Autowired
private CacheManager cacheManager;
public void printCacheStatistics() {
System.out.println("=== 缓存统计信息 ===");
Collection<String> cacheNames = cacheManager.getCacheNames();
for (String cacheName : cacheNames) {
Cache cache = cacheManager.getCache(cacheName);
if (cache instanceof ConcurrentMapCache) {
ConcurrentMapCache concurrentMapCache = (ConcurrentMapCache) cache;
System.out.println("缓存名称: " + cacheName);
System.out.println("缓存大小: " + concurrentMapCache.getNativeCache().size());
}
}
}
public Map<String, Integer> getCacheSizes() {
Map<String, Integer> cacheSizes = new HashMap<>();
Collection<String> cacheNames = cacheManager.getCacheNames();
for (String cacheName : cacheNames) {
Cache cache = cacheManager.getCache(cacheName);
if (cache instanceof ConcurrentMapCache) {
ConcurrentMapCache concurrentMapCache = (ConcurrentMapCache) cache;
cacheSizes.put(cacheName, concurrentMapCache.getNativeCache().size());
}
}
return cacheSizes;
}
}
6.3.2 缓存健康检查
java
@Component
public class CacheHealthIndicator {
@Autowired
private CacheManager cacheManager;
public Health checkCacheHealth() {
Health.Builder builder = Health.up();
try {
Collection<String> cacheNames = cacheManager.getCacheNames();
Map<String, Object> details = new HashMap<>();
for (String cacheName : cacheNames) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
details.put(cacheName + "_status", "UP");
if (cache instanceof ConcurrentMapCache) {
ConcurrentMapCache concurrentMapCache = (ConcurrentMapCache) cache;
details.put(cacheName + "_size", concurrentMapCache.getNativeCache().size());
}
} else {
details.put(cacheName + "_status", "DOWN");
builder.down();
}
}
builder.withDetails(details);
} catch (Exception e) {
builder.down().withException(e);
}
return builder.build();
}
}
6.3.3 缓存管理接口
java
@RestController
@RequestMapping("/cache")
public class CacheManagementController {
@Autowired
private CacheManager cacheManager;
@GetMapping("/names")
public Collection<String> getCacheNames() {
return cacheManager.getCacheNames();
}
@GetMapping("/{cacheName}/size")
public ResponseEntity<Integer> getCacheSize(@PathVariable String cacheName) {
Cache cache = cacheManager.getCache(cacheName);
if (cache instanceof ConcurrentMapCache) {
ConcurrentMapCache concurrentMapCache = (ConcurrentMapCache) cache;
return ResponseEntity.ok(concurrentMapCache.getNativeCache().size());
}
return ResponseEntity.notFound().build();
}
@DeleteMapping("/{cacheName}")
public ResponseEntity<Void> clearCache(@PathVariable String cacheName) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
return ResponseEntity.ok().build();
}
return ResponseEntity.notFound().build();
}
@DeleteMapping("/all")
public ResponseEntity<Void> clearAllCaches() {
Collection<String> cacheNames = cacheManager.getCacheNames();
for (String cacheName : cacheNames) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
}
}
return ResponseEntity.ok().build();
}
}
7. 性能优化建议
7.1 缓存策略优化
java
@Configuration
@EnableCaching
public class OptimizedCacheConfig {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
List<Cache> caches = new ArrayList<>();
// 配置不同大小的缓存
caches.add(new ConcurrentMapCache("smallCache",
new ConcurrentHashMap<>(100), false));
caches.add(new ConcurrentMapCache("mediumCache",
new ConcurrentHashMap<>(1000), false));
caches.add(new ConcurrentMapCache("largeCache",
new ConcurrentHashMap<>(10000), false));
cacheManager.setCaches(caches);
return cacheManager;
}
@Bean
public CacheResolver cacheResolver() {
return new SimpleCacheResolver(cacheManager());
}
}
7.2 缓存预热
java
@Component
public class CacheWarmupService {
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
@EventListener(ApplicationReadyEvent.class)
public void warmupCaches() {
System.out.println("开始预热缓存...");
// 预热用户缓存
List<User> users = userService.getAllUsers();
for (User user : users) {
userService.getUserById(user.getId());
}
// 预热产品缓存
List<Product> products = productService.getAllProducts();
for (Product product : products) {
productService.getProductById(product.getId());
}
System.out.println("缓存预热完成");
}
}
7.3 缓存失效策略
java
@Component
public class CacheEvictionService {
@Autowired
private CacheManager cacheManager;
@Scheduled(fixedRate = 3600000) // 每小时执行一次
public void evictExpiredData() {
System.out.println("执行缓存清理...");
Collection<String> cacheNames = cacheManager.getCacheNames();
for (String cacheName : cacheNames) {
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
// 这里可以实现自定义的过期策略
// 例如:清理超过一定时间的缓存项
System.out.println("清理缓存: " + cacheName);
}
}
}
@EventListener
public void handleDataChangeEvent(DataChangeEvent event) {
// 当数据发生变化时,清理相关缓存
String cacheName = event.getCacheName();
Cache cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.clear();
System.out.println("数据变更,清理缓存: " + cacheName);
}
}
}
8. 高级特性
8.1 分布式缓存支持
java
@Configuration
@EnableCaching
public class DistributedCacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.withCacheConfiguration("userCache",
config.entryTtl(Duration.ofMinutes(10)))
.withCacheConfiguration("productCache",
config.entryTtl(Duration.ofHours(1)))
.build();
}
}
8.2 缓存注解的高级用法
java
@Service
public class AdvancedCacheService {
@Cacheable(value = "userCache", key = "#user.id", unless = "#result == null")
public User getUserById(Long id) {
// 只有当结果不为null时才缓存
return userRepository.findById(id).orElse(null);
}
@Cacheable(value = "userCache", key = "#user.id", condition = "#user.id > 0")
public User getUserByIdWithCondition(Long id) {
// 只有当id大于0时才缓存
return userRepository.findById(id).orElse(null);
}
@CachePut(value = "userCache", key = "#user.id")
public User updateUser(User user) {
// 更新用户并刷新缓存
return userRepository.save(user);
}
@CacheEvict(value = "userCache", key = "#id")
public void deleteUser(Long id) {
// 删除用户并清除缓存
userRepository.deleteById(id);
}
@Caching(evict = {
@CacheEvict(value = "userCache", key = "#user.id"),
@CacheEvict(value = "userListCache", allEntries = true)
})
public void updateUserAndClearListCache(User user) {
// 更新用户并清除多个缓存
userRepository.save(user);
}
}
8.3 自定义缓存注解
java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value = "defaultCache", key = "#root.methodName")
public @interface DefaultCache {
String value() default "defaultCache";
String key() default "";
String condition() default "";
String unless() default "";
}
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Cacheable(value = "shortTermCache", key = "#root.methodName")
public @interface ShortTermCache {
String value() default "shortTermCache";
String key() default "";
}
@Service
public class CustomCacheService {
@DefaultCache(key = "#id")
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
@ShortTermCache(key = "#id")
public String getUserName(Long id) {
User user = userRepository.findById(id).orElse(null);
return user != null ? user.getName() : null;
}
}
9. 最佳实践
9.1 缓存设计原则
java
@Service
public class CacheDesignExample {
// 1. 缓存热点数据
@Cacheable(value = "hotProducts", key = "#id")
public Product getHotProduct(Long id) {
return productRepository.findById(id).orElse(null);
}
// 2. 避免缓存大对象
@Cacheable(value = "productSummary", key = "#id")
public ProductSummary getProductSummary(Long id) {
Product product = productRepository.findById(id).orElse(null);
if (product != null) {
return new ProductSummary(product.getId(), product.getName(), product.getPrice());
}
return null;
}
// 3. 使用合适的缓存键
@Cacheable(value = "userOrders", key = "#userId + '_' + #status")
public List<Order> getUserOrdersByStatus(Long userId, String status) {
return orderRepository.findByUserIdAndStatus(userId, status);
}
// 4. 避免缓存频繁变化的数据
public String getCurrentTime() {
// 不缓存,因为时间经常变化
return LocalDateTime.now().toString();
}
}
9.2 缓存异常处理
java
@Service
public class CacheExceptionHandler {
@Cacheable(value = "userCache", key = "#id", unless = "#result == null")
public User getUserById(Long id) {
try {
return userRepository.findById(id).orElse(null);
} catch (Exception e) {
// 当数据库访问失败时,不缓存异常结果
log.error("获取用户失败: " + id, e);
return null;
}
}
@Cacheable(value = "productCache", key = "#id")
public Product getProductById(Long id) {
try {
return productRepository.findById(id).orElse(null);
} catch (Exception e) {
// 记录异常但不影响缓存
log.error("获取产品失败: " + id, e);
throw e;
}
}
}
9.3 缓存测试
java
@SpringBootTest
class CacheTest {
@Autowired
private UserService userService;
@Autowired
private CacheManager cacheManager;
@Test
void testCacheFunctionality() {
// 第一次调用,应该从数据库获取
User user1 = userService.getUserById(1L);
assertNotNull(user1);
// 第二次调用,应该从缓存获取
User user2 = userService.getUserById(1L);
assertNotNull(user2);
assertEquals(user1.getId(), user2.getId());
// 验证缓存中确实有数据
Cache cache = cacheManager.getCache("userCache");
assertNotNull(cache);
assertNotNull(cache.get(1L));
}
@Test
void testCacheEviction() {
// 先获取用户,确保缓存中有数据
userService.getUserById(1L);
// 删除用户,应该清除缓存
userService.deleteUser(1L);
// 验证缓存已被清除
Cache cache = cacheManager.getCache("userCache");
assertNull(cache.get(1L));
}
}
10. 总结
ApplicationContext在缓存支持方面相比BeanFactory提供了以下重要扩展:
- 声明式缓存支持:通过@EnableCaching和缓存注解提供声明式缓存
- 多种缓存实现:支持内存缓存、Redis缓存等多种实现
- 灵活的缓存策略:支持条件缓存、键生成、过期时间等
- 缓存管理功能:提供缓存监控、统计、清理等功能
- 分布式缓存支持:支持Redis等分布