一、JSR107
JSR是Java Specification Requests 的缩写 ,Java规范请求,故名思议提交Java规范,大家一同遵守这个规范的话,会让大家‘沟通’起来更加轻松, JSR-107呢就是关于如何使用缓存的规范。
Java Caching定义了5个核心接口,分别是CachingProvider,CacheManager,Cache,Entry和Expiry。
CachingProvider定义了创建、配置、获取和控制多个CacheManager。一个应用可以在运行期间访问多个CachingProvider。
CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
Entry是一个存储在Cache中的key-value对。
Expiry每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个有效期,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
二、Spring缓存抽象
Spring从3.1开始定义了org.springframework.cache.Cache 和org.springframework.cache.CacheManager接口来统一不同的缓存技术; 并支持使用JCache(JSR-107)注解简化我们开发;
• Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
• Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;
• 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否 已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法 并缓存结果后返回给用户。下次调用直接从缓存中获取。
• 使用Spring缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据
三、缓存注解
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、 ConcurrentMapCache等 CacheManager 缓存管理器,管理各种缓存(Cache)组件 |
---|---|
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存。 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
四、@Cacheable、@CachePut和@CacheEvict注解的主要参数
value | 缓存的名称,必须指定至少一个 | 例如:@Cacheable(value=“mycache”)或者@Cacheable(value={“cache1”,“cache2”}) |
---|---|---|
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的 所有参数进行组合 | 例如: @Cacheable(value=”mycache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返 回 true 或者 false,只有为 true 才进行缓存/清 除缓存 | 例如: @Cacheable(value=”testcache”,condition=”#userNam e.length()>2”) |
allEntries (@CacheEvict ) | 是否清空所有缓存内容,缺省为 false,如果指 定为 true,则方法调用后将立即清空所有缓存 | 例如: @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation (@CacheEvict) | 是否在方法执行前就清空,缺省为 false,如果 指定为 true,则在方法还没有执行的时候就清 空缓存,缺省情况下,如果方法执行抛出异常, 则不会清空缓存 | 例如: @CachEvict(value=”testcache”, beforeInvocation=true) |
五、@Cacheable注解
要使@Cacheable注解生效要在配置类上加@EnableCaching注解
@Cacheable注解的作用是将方法的运行结构进行缓存,以后再要相同的数据,直接从缓存中拿,不用调用方法。
@Cacheable注解的几个属性
cacheNames/value:指定缓存名字,CacheManager管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一名字。
key:缓存数据使用的key,可以用它来指定,默认是使用方法的参数值(支持SPEL表达式)。
key="#id"效果 == "#root.args[0]" == "#a0"
keyGenerator:key的生成器,可以自己指定key的生成器,keyGenerator/key:二选一。
cacheManager:指定缓存管理器。
cacheResolver:和cacheManager一样,二选一。
condition:指定符合条件的情况下,才缓存,可以用SPEL表达式。
unless:否定缓存,当unless指定的条件为true,方法的返回值就不会缓存,可以取到结果进行判断。
sync:缓存是否使用异步模式,在改没事下,unless属性没用。
SPEL表达式能写的内容如下:
名字 | 位置 | 位置 | 示例 |
---|---|---|---|
methodName(方法名字) | root Object | 当前被调用的方法名 | 当前被调用的方法名 |
method(方法) | root Object | 当前被调用的方法 | #root.method |
target(目标对象) | root Object | 当前被调用的目标对象 | #root.target |
targetClass(目标对象类) | root Object | 当前被调用的目标对象类 | #root.targetClass |
args(参数列表) | root Object | 当前被调用的方法的参数列表 | #root.args[0] |
caches(缓存列表) | root Object | 当前方法调用的缓存列表 | #root.cache[0].name |
argument name(方法参数的名字) | evaluation context | 方法参数的名字,可以直接#参数名 | #a0,#p0 |
result | evaluation context | 方法执行后的返回值(仅当方法执行后的判断有效,如"unless","cache put"的表达式"cache evict"的表达式beforeInvocation=false) | #result |
如下代码:
当传入的id大于零时且返回结果不为空时,结果才会被缓存。
缓存原理:
在springboot中如果要看与Cache相关原理,肯定有xxxAutoConfiguraiot与之对应。
其中最重要的是为IOC容器中导入了CacheConfigurationImportSelector(缓存配置导入选择器)这个类。
这个类中有一个selectImports(AnnotationMetadata importingClassMetadata)方法,这个方法的作用就是为容器中导入缓存组件。我们可以打个断点看一下:
我们可以看到这个方法一共为容器中导入了11个缓存组件,但是这11个缓存组件不都是全部生效的,我们只需在配置文件中加上如下属性:
这个配置的作用就是让程序在控制台中打印出哪些组件生效,哪些组件不生效。
我们可以看到是SimpleCacheConfiguration这个缓存配置类生效。
我们可以看到SimpleCacheConfiguration为容器中导入了一个缓存管理器(ConcurrentMapCacheManager),ConcurrentMapCacheManager实现了CacheManager接口。
ConcurrentMapCacheManager中的getCache()方法是通过缓存的名字来得到缓存对象的,如果得到的换成对象为null,就调用createConcurrentMapCache(name)方法。
这个方法创建了一个ConcurrentMapCache类的对象,这个类实现了Cache接口,内部通过维护一个ConcurrentMap来实现数据的换成与读取。
@Cacheable注解的运行流程
运行流程:
1.方法运行之前,先去查询Cache,按照cacheNames指定的名字获取(CacheManager先获取相应的缓存),第一次获取缓存,如果没有Cache组件会自动创建。
2.去Cache中查找缓存的内容,使用key,默认使用方法的参数
3.没有查询到缓存就调用目标方法,
4.将目标方法返回的结果,放进缓存里面
那为什么默认是使用方法的参数呢?它默认是使用SimpleKeyGenerator类的generate(Object target, Method method, Object… params) 来生成key。
方法中的第一个参数是目标对象,第二个参数是目标方法,第三个参数是目标方法的参数列表。
六、@CachePut注解
@CachePut: 既调用方法,又更新缓存数据
修改了数据库的某个数据,同时更新缓存
运行时机:
1.先调用目标方法
2.将目标方法的结果缓存起来
如下图:
七、@CachePut注解
@CacheEvict:缓存清除
key:指定要清除的数据
allEntries=true:指定清除这个缓存中的所有数据
beforeInvocation=true:表示在方法执行之前执行缓存清除。beforeInvocation=false情况下,如果方法出错,那么缓存就不会清除,beforeInvocation=true情况下,那么缓存就会清除
八、@Cachingt注解
@Caching是一个组合注解
九、@CacheConfig注解
@CacheConfig注解里面的属性
cacheNames:指定公共的缓存名字,这样就不需要再@Cacheable、@CachePut和@CacheEvict等注解里面指定cacheNames属性
keyGenerator:指定公共的key生成器
cacheManager:指定公共的缓存管理器
cacheResolver:指定公共的缓存解析器
十、SpringBoot整合Redis
可以看我的另一篇文章SpringBoot整合Redis