Springboot-Cache-缓存
一. 缓存抽象
从 3.1 版本开始,Spring 框架提供了对现有 Spring 应用透明地添加缓存的支持。与 事务 支持类似,缓存抽象允许一致使用各种缓存解决方案,对代码的影响最小。
在 Spring Framework 4.1 中,缓存抽象得到了极大的扩展,支持 JSR-107 注解 和更多的自定义选项。
缓存服务是一个抽象(而不是缓存实现),需要使用实际的存储来存储缓存数据—也就是说,这个抽象让你不必编写缓存逻辑,但不提供实际的数据存储。这种抽象由 org.springframework.cache.Cache
和 org.springframework.cache.CacheManager
接口具体化。
Spring 提供了 一些该抽象的实现: 基于 JDK java.util.concurrent.ConcurrentMap 的缓存、Gemfire 缓存、 Caffeine 以及符合JSR-107标准的缓存(如 Ehcache 3.x)。
中文文档:https://springdoc.cn/spring/integration.html#cache
二. 相关 API
1. Cache
表示一个缓存,缓存内容的单位。有以下实现:
以 ConcurrentMapCache 为例:
public class ConcurrentMapCache extends AbstractValueAdaptingCache { //缓存的名字,不知道有啥用 private final String name; //使用 ConcurrentMap 来存储缓存,每个键值对表示一个缓存。其中键对应 @Cacheable(key="") Key 的值,值就是缓存的内容 private final ConcurrentMap<Object, Object> store; @Nullable private final SerializationDelegate serialization; ... }
相当于多个缓存键值对的结合,其他实现类没看,应该也差不多是这样吧。。。
2. CacheManager
常规的SpringBoot已经为我们自动配置了EhCache、Collection、Guava、ConcurrentMap等缓存,默认使用ConcurrentMapCacheManager。SpringBoot的application.properties配置文件,使用spring.cache前缀的属性进行配置。
缓存管理器,有以下实现:
同样以 ConcurrentMapCacheManager 为例:
public class ConcurrentMapCacheManager implements CacheManager, BeanClassLoaderAware { //内部持有一个 concurrentMap,这个 map 的键值对 key-value,key 就是每个缓存的名字,value 则是每个上述的 Cache private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16); private boolean dynamic = true; private boolean allowNullValues = true; private boolean storeByValue = false; ... }
每个 Spring 可以配置多个 CacheManager,但只能自动装配一个,否则会报错。
//这种就会出现异常,无法启动项目 @Configuration public class CacheConfig { @Bean public CacheManager concurrentMapCacheManager(){ return new ConcurrentMapCacheManager("aaa", "bbb"); } @Bean public CacheManager simpleCacheManager(){ return new SimpleCacheManager(); } }
需要使用 @Bean(autowireCandidate = false)
来禁止 bean 自动装配,然后要使用禁止自动装配的 CacheMange,就需要在 @Cacheable 上指定 cacheManger 属性。
3. Cache 和 CacheManager 的关系
每个 Cache 好比一组缓存内容,内部是一个 map 来维护。而 CacheManager 则管理了很多个 Cache,通过 CacheManage 的 cacheName 或 value 指定使用哪个 Cache,而 key 指定 Cache 中的哪个键值对。
三. 基于声明式注解缓存
1. @Cacheable
使用 @Cacheable 来划分可缓存的方法—也就是那些结果被存储在缓存中的方法,这样,在后续的调用中(使用相同的参数),缓存中的值就会被返回,而无需实际调用该方法。每次调用该方法时,都会检查缓存,看是否已经运行过该调用,而不必重复。虽然在大多数情况下,只有一个缓存被声明,但注解允许指定多个名字,这样就可以使用多个缓存了。在这种情况下,每个缓存在调用方法之前都会被检查—如果至少有一个缓存被命中,相关的值会被返回。
所有其他不包含该值的缓存也会被更新,即使该缓存方法没有被实际调用。
1. cacheManager
指明某个固定的 CacheManager
2. cacheResolver
该属性,用来指定缓存管理器。使用配置同 cacheManager 类似,可自行百度。(cacheManager指定管理器/cacheResolver指定解析器 它俩也是二选一使用),不深究
3. value
缓存 Cache 的名称,支持 spel 表达式。
4. cacheNames
同 value
5. key
缓存 Cache 中的键值,默认规则:
- 如果请求没有参数:key=new SimpleKey();
- 如果请求有一个参数:key=参数的值
- 如果请求有多个参数:key=newSimpleKey(params);
key值的编写,可以使用 SpEL 表达式的方式来编写:
名字 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root object | 当前被调用的方法名 | #root.method.name |
method | root object | 当前被调用的方法 | #root.methodName |
target | root object | 当前被调用的目标对象 | #root.target |
targetClass | root object | 当前被调用的目标对象类 | #root.targetClass |
args | root object | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root object | 当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”,“cache2”})),则有两个cache | #root.caches[0].name |
argument name | evaluation context | 方法参数的名字. 可以直接 #参数名 ,也可以使用 #p0或#a0 的形式,0代表参数的索引; | #id、#p0、#a0 |
result | evaluation context | 方法执行后的返回值(仅当方法执行之后的判断有效,如’unless’、'cache put’的表达式 'cacheevict’的表达式beforeInvocation=false) | #result |
//类名+方法名+参数 @Cacheable(cacheNames = "compare", key = "#root.targetClass + '--' + #root.method.name + '--' + #modelId + '--' + #modelVersion", cacheManager = "redisCacheManager") public Set<String> findPropertiesByModelMsg(Integer modelId, Integer modelVersion){}
6. keyGenerator
key 的生成器。如果觉得通过参数的方式来指定比较麻烦,我们可以自己指定 key 的生成器的组件 id。key/keyGenerator属性:二选一使用。不深究
7. condition
条件判断属性,用来指定符合指定的条件下才可以缓存。也可以通过 SpEL 表达式进行设置。这个配置规则和 key 属性表格中的配置规则是相同的。
8. unless
unless属性,意为"除非"的意思。即只有 unless 指定的条件为 true 时,方法的返回值才不会被缓存
9. sync
该属性用来指定是否使用异步模式,该属性默认值为 false,默认为同步模式。异步模式指定 sync = true 即可,异步模式下 unless 属性不可用。
2. @CachePut
当需要更新缓存而不干扰方法的执行时,你可以使用 @CachePut 注解。也就是说,该方法总是被调用,其结果被放入缓存(根据 @CachePut 选项)。它支持与 @Cacheable 相同的选项,应该用于缓存的填充而不是方法流的优化。
最好不要和 @Cacheable 一起用
3. @CacheEvict
缓存抽象不仅允许缓存存储,而且还允许驱逐。这个过程对于从缓存中移除陈旧或未使用的数据很有用。与 @Cacheable 相反,@CacheEvict 划分了执行缓存驱逐的方法(也就是说,作为从缓存中移除数据的触发器的方法)。与它的兄弟姐妹类似,@CacheEvict 需要指定一个或多个受行动影响的缓存,允许指定一个自定义的缓存和 key 解析或条件,并具有一个额外的参数(allEntries),表明是否需要执行整个缓存的驱逐,而不仅仅是一个条目的驱逐(基于 key)
1. allEntries
使用 allEntries = true 属性来驱逐缓存中的所有条目
2. beforeInvocation
beforeInvocation=true 导致驱逐总是在方法被调用之前发生。这在驱逐不需要与方法结果相联系的情况下很有用。
4. @Caching
有时,同一类型的多个注解(如 @CacheEvict 或 @CachePut)需要被指定—例如,因为不同缓存的 condition 或 key 表达式是不同的。@Caching 允许在同一个方法上使用多个嵌套的 @Cacheable、@CachePut 和 @CacheEvict 注解
@Caching(evict = { @CacheEvict("primary"), @CacheEvict(cacheNames="secondary", key="#p0") }) public Book importBooks(String deposit, Date date)
5. @CacheConfig
@CacheConfig 是一个类级注解,它允许共享缓存名称、自定义 KeyGenerator、自定义 CacheManager 和自定义 CacheResolver。把这个注解放在类上并不开启任何缓存操作。
一个操作级别的自定义总是覆盖 @CacheConfig 上的自定义设置。因此,这为每个缓存操作提供了三个层次的自定义:
- 全局配置,可用于 CacheManager、KeyGenerator。
- 在类级别,使用 @CacheConfig。
- 在操作级别上。
6. @EnableCaching
启用缓存,用在配置类或启动类上
四. 使用示例
1. 配置缓存配置类
配置两个 cacheManager,只能装配一个,所以其他的 bean 使用 autowireCandidate=false 进制自动装配。因为这里配置的是 concurrentMapCacheManager 和 simpleCacheManager,所以不用额外依赖。
@Configuration public class CacheConfig { @Bean(autowireCandidate = false) public CacheManager concurrentMapCacheManager(){ return new ConcurrentMapCacheManager("aaa", "bbb"); } @Bean public CacheManager simpleCacheManager(){ return new SimpleCacheManager(); } }
2. 控制层
@RestController @RequestMapping("cache") public class CacheController { @Autowired private CacheService cacheService; @Autowired private CacheManager cacheManager; @RequestMapping("/test") public void test(){ System.out.println( cacheService.addNum(1, 2) ); } @RequestMapping("/s") public void test1(){ System.out.println( cacheManager ); } }
3. 业务层
使用 @Cacheable 来配置缓存
@Service @Slf4j public class CacheService { @Cacheable(cacheNames = "aaa", key = "'yqkey'", cacheManager = "concurrentMapCacheManager") public int addNum(int num1, int num2){ log.info("参数:num1={},num2={}", num1, num2); //模拟业务需要的时间 try { Thread.sleep(1_000); } catch (InterruptedException e) { e.printStackTrace(); } return num1 + num2; } }
4. 启动类
@SpringBootApplication @EnableCaching public class CacheMain { public static void main(String[] args) { SpringApplication.run(CacheMain.class, args); } }
本文作者:Hi.PrimaryC
本文链接:https://www.cnblogs.com/cnff/p/17606411.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步