【附录】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等分布
相关推荐
尘鹄4 小时前
go 初始化组件最佳实践
后端·设计模式·golang
墩墩分墩4 小时前
【Go语言入门教程】 Go语言的起源与技术特点:从诞生到现代编程利器(一)
开发语言·后端·golang·go
程序员爱钓鱼7 小时前
Go语言实战案例- 开发一个ToDo命令行工具
后端·google·go
学渣676567 小时前
文件传输工具rsync|rust开发环境安装|Ascend实验相关命令
开发语言·后端·rust
我是渣哥7 小时前
Java String vs StringBuilder vs StringBuffer:一个性能优化的探险故事
java·开发语言·jvm·后端·算法·职场和发展·性能优化
晚安里8 小时前
JVM相关 4|JVM调优与常见参数(如 -Xms、-Xmx、-XX:+PrintGCDetails) 的必会知识点汇总
java·开发语言·jvm·后端·算法
齐 飞10 小时前
SpringBoot实现国际化(多语言)配置
java·spring boot·后端
FrankYoou11 小时前
Spring Boot + Spring MVC 项目结构
spring boot·spring·springmvc
David爱编程11 小时前
锁升级机制全解析:偏向锁、轻量级锁、重量级锁的秘密
java·后端
技术小泽11 小时前
深度解析Netty架构工作原理
java·后端·性能优化·架构·系统架构