Spring @Cacheable 解读

前言

@Cacheable 是 Spring 框架中用于方法级缓存的注解。它主要用于减少方法调用的次数,通过缓存方法的返回结果来提高应用程序的性能。

核心原理是,Spring 提供了统一的标准抽象,可以插拔式接入不同的缓存实现。但对于开发使用时,无需关注底层使用哪套缓存源,上层使用时的都是标准一套方式,如 @Cacheable ...

这便是 Spring Cacheable 存在的价值与意义。

流程

作用

  • 缓存结果:@Cacheable 注解的方法在第一次调用时会将结果缓存起来,后续调用相同参数的方法时,会直接从缓存中获取结果,而不再执行方法体。
  • 提高性能:通过减少重复计算和数据库查询等耗时操作,提高应用程序的响应速度和性能。
  • 透明性:开发者无需显式管理缓存,Spring 自动处理缓存的存储和检索。

原理

@Cacheable 的工作机制依赖于 Spring 的缓存抽象层。以下是其工作原理的关键点:

  1. 缓存管理器:Spring 提供了多种缓存管理器(如 ConcurrentMapCacheManager、EhCacheCacheManager、RedisCacheManager 等),用于管理缓存的存储和检索。
  2. 缓存解析:当带有 @Cacheable 注解的方法被调用时,Spring 会检查缓存中是否存在对应的结果。如果存在,则直接返回缓存结果;如果不存在,则执行方法体并将结果存入缓存。
  3. 缓存键:默认情况下,缓存键是根据方法参数自动生成的。可以通过 key 属性自定义缓存键。
  4. 条件缓存:可以通过 condition 属性指定条件表达式,只有满足条件时才会缓存结果。
  5. 缓存命名:通过 value 或 cacheNames 属性指定缓存的名称,以便在不同的缓存中存储结果。

用法

以下是 @Cacheable 的基本用法示例:

java 复制代码
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product getProductById(Long id) {
        ...
        
        // biz logic 
        
        ...
    }
}

关键属性

  • value/cacheNames:指定缓存的名称。
  • key:自定义缓存键的生成策略。
  • condition:指定缓存条件,只有满足条件时才会缓存结果。
  • unless:指定条件表达式,只有不满足条件时才会缓存结果。

配置

要使用 @Cacheable,需要在 Spring 配置中启用缓存支持:

java 复制代码
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableCaching
public class CacheConfig {
    // 可以在这里配置缓存管理器
}

缓存抽象层

Spring 的缓存抽象层提供了一种统一的方式来管理不同的缓存实现。

它通过定义一组标准接口和注解,使得开发者可以轻松地在应用程序中集成缓存功能,而无需关心底层缓存技术的具体实现

这种抽象层的设计使得应用程序可以在不同的缓存解决方案之间切换,而无需修改业务逻辑代码。

缓存抽象层的核心组件

  1. Cache 接口

    • 定义了缓存的基本操作,如获取、放置和删除缓存条目。
    • 常用方法包括 get(Object key)、put(Object key, Object value) 和 evict(Object key)。
  2. CacheManager 接口

    • 管理多个 Cache 实例,提供对缓存的访问。
    • 负责根据缓存名称获取对应的 Cache 实例。
    • 常用方法包括 getCache(String name) 和 getCacheNames()。
  3. CacheResolver 接口

    • 用于解析缓存的策略,决定使用哪个缓存。
    • 可以自定义实现以满足复杂的缓存解析需求。
  4. CacheErrorHandler 接口

    • 定义了处理缓存操作异常的策略。
    • 可以自定义实现以处理缓存操作中的异常情况。

缓存注解

Spring 提供了一组注解来简化缓存操作:

  • @Cacheable:用于标记方法的返回结果可以被缓存。
  • @CachePut:用于更新缓存,不影响方法的执行。
  • @CacheEvict:用于从缓存中移除一个或多个条目。
  • @Caching:用于组合多个缓存操作注解。

缓存管理器

缓存管理器(CacheManager)是 Spring 缓存抽象层的核心组件之一。它负责管理应用程序中的多个缓存实例,并提供对这些缓存的统一访问接口。

通过缓存管理器,开发者可以轻松地在应用程序中集成和管理缓存功能。

缓存管理器的核心功能

  1. 缓存实例管理

    • 缓存管理器负责创建和管理多个缓存实例。每个缓存实例通常对应一个特定的缓存区域或命名空间。
  2. 缓存访问接口

    • 提供统一的接口来访问和操作缓存实例。开发者可以通过缓存管理器获取特定名称的缓存,并对其进行操作。
  3. 缓存配置

    • 缓存管理器允许配置缓存的行为,如过期策略、存储策略和序列化策略等。

常见的缓存管理器实现

Spring 提供了多种缓存管理器实现,支持不同的缓存存储技术:

  1. ConcurrentMapCacheManager

    • 基于 Java 的 ConcurrentHashMap 实现的简单内存缓存管理器。
    • 适合开发和测试环境,不适合生产环境的分布式缓存需求。
  2. EhCacheCacheManager

    • 支持 EhCache 的缓存管理器,适合需要持久化和高级缓存特性的场景。
  3. RedisCacheManager

    • 支持 Redis 的缓存管理器,适合分布式缓存场景,提供高性能和可扩展性。
  4. CaffeineCacheManager

    • 支持 Caffeine 的缓存管理器,提供高性能的内存缓存,适合需要低延迟的应用。
  5. GuavaCacheManager

    • 支持 Guava 的缓存管理器,适合简单的内存缓存需求。

默认缓存管理器?

如果不显示指定缓存管理器,系统会默认使用哪个缓存管理器?

如果在 Spring 应用中启用了缓存功能(通过 @EnableCaching 注解),但没有显式配置缓存管理器,Spring 会使用默认的缓存管理器。

具体来说,Spring Boot 会自动配置一个基于 ConcurrentMapCacheManager 的缓存管理器。

默认缓存管理器:ConcurrentMapCacheManager

  • 实现:ConcurrentMapCacheManager 是基于 Java 的 ConcurrentHashMap 实现的简单内存缓存管理器。

  • 特性

    • 线程安全:由于使用了 ConcurrentHashMap,它是线程安全的。
    • 简单易用:适合开发和测试环境,因为它不需要任何外部依赖。
    • 非持久化:缓存数据存储在内存中,应用程序重启后数据会丢失。
    • 单节点:不支持分布式缓存,仅适用于单节点应用。

使用场景

  • 开发和测试:由于其简单性和易用性,ConcurrentMapCacheManager 非常适合在开发和测试阶段使用。
  • 小型应用:对于不需要分布式缓存和持久化的简单应用,也可以使用这种默认缓存管理器。

指定缓存管理器

当然,最常见的、也是最推荐的是显示指定缓存管理器。

以下是配置 RedisCacheManager 的示例:

java 复制代码
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

@Configuration
@EnableCaching
public class RedisCacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(60)) // 设置缓存过期时间
                .disableCachingNullValues()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));

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

配置多个缓存管理器?

如果配置了多个缓存管理器,系统如何选择?

在 Spring 应用中,如果配置了多个缓存管理器,系统需要一种机制来选择使用哪个缓存管理器。Spring 的缓存抽象

通常有几种方式,如下:

选择缓存管理器的机制

  1. 默认缓存管理器

    • 如果没有特别指定,Spring 会使用默认的缓存管理器。默认缓存管理器通常是通过 @Primary 注解标记的缓存管理器。
    • 如果没有标记 @Primary,Spring 会尝试使用第一个找到的缓存管理器。
  2. 自定义 CacheResolver

    • 你可以实现 CacheResolver 接口来自定义缓存解析逻辑。通过自定义 CacheResolver,可以根据方法参数、注解属性或其他上下文信息来动态选择缓存管理器。
    • 自定义的 CacheResolver 可以通过 @Cacheable、@CachePut 和 @CacheEvict 注解的 cacheResolver 属性来指定。

自定义处理器:

java 复制代码
@Configuration
public class CustomCacheResolverConfig {

    @Bean
    public CacheResolver customCacheResolver(CacheManager memoryCacheManager, CacheManager redisCacheManager) {
        // 自定义逻辑来选择缓存管理器
        return new SimpleCacheResolver(memoryCacheManager); // 示例中简单使用内存缓存
    }
}

使用缓存时,指定处理器:

java 复制代码
@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id", cacheResolver = "customCacheResolver")
    public Product getProductById(Long id) {
        
        // ...
        
    }
}
  1. 使用缓存时,指定缓存管理器 cacheManager
java 复制代码
@Cacheable(cacheNames = "getItemDetail", cacheManager = "simpleCacheManager", unless = "#result.size() == 0")
public Collection<BillItemDTO> getItemDetail() {
 
   // ...
    
}

如果有多个缓存管理器,这里推荐指定缓存管理器,更加直观易懂.

总结

@Cacheable 是一个强大的注解,用于在 Spring 应用中实现方法级缓存。通过缓存方法的返回结果,可以显著提高应用程序的性能。

Spring 提供了灵活的缓存管理机制,支持多种缓存存储方案,并允许开发者自定义缓存键和条件。

Spring 通过抽离抽象层,支持多套不同缓存,我们也可以同时使用多套不同的缓存以满足不同需求。

相关推荐
David爱编程41 分钟前
为什么必须学并发编程?一文带你看懂从单线程到多线程的演进史
java·后端
long3161 小时前
java 策略模式 demo
java·开发语言·后端·spring·设计模式
xiao-xiang1 小时前
redis-sentinel基础概念及部署
数据库·redis·sentinel
云间月13142 小时前
飞算JavaAI:从智能调度到出行服务的全链路技术升级
java·redis·飞算javaai炫技赛
rannn_1112 小时前
【Javaweb学习|黑马笔记|Day1】初识,入门网页,HTML-CSS|常见的标签和样式|标题排版和样式、正文排版和样式
css·后端·学习·html·javaweb
柏油3 小时前
Spring @TransactionalEventListener 解读
spring boot·后端·spring
两码事4 小时前
告别繁琐的飞书表格API调用,让飞书表格操作像操作Java对象一样简单!
java·后端
shark_chili5 小时前
面试官再问synchronized底层原理,这样回答让他眼前一亮!
后端
灵魂猎手5 小时前
2. MyBatis 参数处理机制:从 execute 方法到参数流转全解析
java·后端·源码