【附录】Spring 缓存支持 基础及应用

此文是 【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提供了以下重要扩展:

  1. 声明式缓存支持:通过@EnableCaching和缓存注解提供声明式缓存
  2. 多种缓存实现:支持内存缓存、Redis缓存等多种实现
  3. 灵活的缓存策略:支持条件缓存、键生成、过期时间等
  4. 缓存管理功能:提供缓存监控、统计、清理等功能
  5. 分布式缓存支持:支持Redis等分布
相关推荐
Moonbit1 小时前
MoonBit 作者寄语 2025 级清华深圳新生
前端·后端·程序员
前端的阶梯1 小时前
开发一个支持支付功能的微信小程序的注意事项,含泪送上
前端·后端·全栈
咕噜分发企业签名APP加固彭于晏1 小时前
腾讯元器的优点是什么
前端·后端
AAA修煤气灶刘哥2 小时前
Swagger 用着糟心?试试 Knife4j,后端开发狂喜
后端·面试
bobz9652 小时前
MCP on windows
后端
泡海椒2 小时前
jquickexcel 全功能指南:从数据导入到精美导出的完整流程
后端
iOS开发上架哦3 小时前
移动端网页调试实战,键盘弹出与视口错位问题的定位与优化
后端
百度Geek说3 小时前
PaddleMIX推出扩散模型推理加速Fast-Diffusers:自研蒸馏加速方法FLUX-Lightning实现4步图像生成
后端
gopher_looklook3 小时前
Go并发实战:singleflight 源码解读与二次封装
数据结构·后端·go
用户833810251223 小时前
我为什么做PmMock:让接口设计不再头疼
前端·后端