JSR107
Java Caching 定义了5个核心接口 分别是 CachingProvider,CachManager、Cache、Entry、Expiry。
- CachingProvider 定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期间访问多CachingProvider。
- CacheManger 定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache 存在于CacheManager 的上下文中,一个CacheManager 仅被一个CacheingProvider 所拥有。
- Cache 是一个类似Map的数据结构并临时存储以key 为索引的值。一个Cache 仅被一个CacheManager 所拥有
- Entry 是一个存储在Cache中的key-value对
- Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个有效期,条目变为过期状态。一旦过期,条目将不可访问、更新、和删除。缓存有效期可以通过 ExpiryPolicy 设置。
开发中使用JSR-107需要导入包:一般不直接使用JSR-107开发,因为JSR-107仅仅定义了接口,而没有实现
<!-- https://mvnrepository.com/artifact/javax.cache/cache-api -->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.1.0</version>
</dependency>
Spring缓存抽象
Spring 从3.1 开始支持 JSR-107 注解 简化我们开发
- org.springframework.cache.Cache
- org.springframework.cache.CacheManager
- Cache接口为缓存的组件规范定义,包含缓存的各种操作集合
-
Cache接口下Spring提供了各种xxxCache的实现;如RedisCache、EhCacheCache、ConcurrentMapCache等;
image.png
执行原理:每次调用需要缓存功能的方法时,Spring会检查指定参数的目标方法是否已经被调用过,如果有直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果返回给用户。下次调用直接从缓存中获取。
使用Spring缓存抽象时我们需要关注以下两点:
- 确定方法需要被缓存以及他们都缓存策略
- 从缓存中读取之前都缓存存储都数据
Spring 缓存开发重要概念
Spring 缓存开发相关概念 | 作用功能 |
---|---|
Cache | 缓存接口,定义缓存操作。实现有 如RedisCache、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 缓存管理器,管理各种缓存(Cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法都请求参数对其结果进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存 |
@EnableCaching | 开启基于注解的缓存 |
KeyGenerator | 缓存数据时Key生成策略 |
serilalize | 缓存数据时value序列化策略 |
SpringBoot 整合 Spring缓存抽象
基于 spring-boot-starter 2.4.0
springCache官方文档:https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/integration.html#cache
搭建Mybatis crud 环境(略)
- 导入相关 pom文件
<!-- Spring 缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
-
开启基于缓存的注解 @EnableCaching
-
标注缓存注解 @Cacheable(cacheNames = {"xxx"}
@Cacheable(cacheNames = {"stu"})
@Override
public Student getStu(Integer id) {
Student student = studentMapper.selectByPrimaryKey(id);
System.out.println(student);
return student;
}
如果只标注 @Cacheable 会报异常: At least one cache should be provided per cache operation.
@Cacheable
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
@AliasFor("cacheNames")
String[] value() default {};
@AliasFor("value")
String[] cacheNames() default {};
String key() default "";
String cacheManager() default "";
String cacheResolver() default "";
String condition() default "";
String unless() default "";
boolean sync() default false;
cacheNames/value :指定缓存组件对名字;CacheManager 管理多个Cache 组件,每一个缓存组件有自己唯一一个名字
@Cacheable(cacheNames = {"stu"})
@Override
public Student getStu(Integer id) {
Student student = studentMapper.selectByPrimaryKey(id);
System.out.println(student);
return student;
}
key:缓存数据的 key,默认是使用方法的参数的值。
比如:使用方法名字getStu和方法参数拼接 key:getStu[1],编写SpEl:#id; 参数id的值 #a0 #p0 # root.args[0]
@Cacheable(cacheNames = {"stu"}, key = "#root.methodName+'['+#id+']'")
@Override
public Student getStu(Integer id) {
Student student = studentMapper.selectByPrimaryKey(id);
System.out.println(student);
return student;
}
debug模式下可以看下如下结果:
image.png image.png
keyGenerator:key的生成器,可以自己指定key的生成器的组件id
编写配置类
@Component(value = "MyKeyGenerator")
public class MyKeyGenerator implements KeyGenerator{
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName()+"["+ Arrays.asList(params).toString()+"]";
}
}
keyGenerator = "MyKeyGenerator"
@Cacheable(cacheNames = {"stu"}, keyGenerator = "MyKeyGenerator")
@Override
public Student getStu(Integer id) {
Student student = studentMapper.selectByPrimaryKey(id);
System.out.println(student);
return student;
}
cacheManager:指定缓存管理器
cacheResolver:指定缓存解析器
condition :指定符合条件的情况下才缓存
id为1时才缓存
@Cacheable(cacheNames = {"student"}, keyGenerator = "MyKeyGenerator", condition = "#a0 == 1")
@Override
public Student getStu(Integer id) {
Student student = studentMapper.selectByPrimaryKey(id);
System.out.println(student);
return student;
}
unless :否定缓存:当unless 指定的条件为true,方法的返回值就不会被缓存;
@Cacheable(cacheNames = {"stu"}, keyGenerator = "MyKeyGenerator", condition = "#a0 == 1", unless = "#a0 == 1")
@Override
public Student getStu(Integer id) {
Student student = studentMapper.selectByPrimaryKey(id);
System.out.println(student);
return student;
}
注意:满足condition条件 又满足 unless 是不缓存的 unless 优先
sync: 是否使用异步模式默认是false 如果开启为true 则unless 属性就不能得到支持
可以获取 到结果进行判断 unless = “#result == null”
@CachePut
既调用方法又更新缓存。修改了数据库某个数据,同时更新缓存
- 先调用目标方法
- 将目标方法的结果缓存起来
@CachePut注解中的属性和 @Cacheable中的属性几乎是一样的就不在展开
@CachePut(value = "stu", key = "#student.id")
@Override
public Student updateStu(Student student){
System.out.println(student);
studentMapper.updateByPrimaryKey(student);
return student;
}
key还可以写成 key = "#result.id",因为先调用方法然后把结果缓存起来,所以可以拿到结果取出id
注意: 一定要指定key 不然会按默认值方法的参数 新生成一个 k,并把结果缓存
@CacheEvict
allEntries 默认 false 是否清空所有缓存
beforeInvocation 默认 false 缓存清除操作在方法之后执行,如果方法异常,则缓存不会清除
@CacheEvict(value = "#id", allEntries = true, beforeInvocation = true)
public void delSut(Integer id) {
System.out.println(id);
studentMapper.deleteByPrimaryKey(id);
}
@Caching
组合多个Cache注解使用
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
例子:
@Caching(
cacheable = {@Cacheable(value = "stu",key = "#userName")},
put = {@CachePut(value = "stu", key = "#result.id"),
@CachePut(value = "stu", key = "#result.age")
}
)
public Student getStuByStr(String userName) {
StudentExample studentExample = new StudentExample();
studentExample.createCriteria().andUserNameEqualTo(userName);
List<Student> students = studentMapper.selectByExample(studentExample);
return Optional.ofNullable(students).orElse(null).get(0);
}
@CacheConfig 抽取缓存的公共配置
我们每个缓存注解中 都指定 了value = "stu" / cacheNames="stu" ,可以抽离出来,在整个类上添加
@CacheConfig(cacheNames = "stu"),之后每个方法中都默认使用 cacheNames = “stu”
@CacheConfig(cacheNames = "stu")
@Service
public class StudentServiceImpl implements StudentService {
@Resource
private StudentMapper studentMapper;
@CachePut(key = "#result.id")
@Override
public Student updateStu(Student student){
System.out.println(student);
studentMapper.updateByPrimaryKey(student);
return student;
}
/**
* Cacheable
* @param id
* @return
*
* key = "#root.methodName+'['+#id+']'"
*/
@Cacheable(key = "#id")
@Override
public Student getStu(Integer id) {
Student student = studentMapper.selectByPrimaryKey(id);
System.out.println(student);
return student;
}
@CacheEvict(allEntries = true, beforeInvocation = true)
public void delSut(Integer id) {
System.out.println(id);
studentMapper.deleteByPrimaryKey(id);
}
@Caching(
cacheable = {@Cacheable(key = "#userName")},
put = {@CachePut(key = "#result.id"),
@CachePut(key = "#result.age")
}
)
public Student getStuByStr(String userName) {
StudentExample studentExample = new StudentExample();
studentExample.createCriteria().andUserNameEqualTo(userName);
List<Student> students = studentMapper.selectByExample(studentExample);
return Optional.ofNullable(students).orElse(null).get(0);
}
}
Cache SpEL available metadata
名字 | 位置 | 描述 | 实例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法名 | #root.methodname |
method | root对象 | 当前被调用的方法 | #root.method.name |
target | root对象 | 当前被调用的目标对象实例 | #root.target |
targetClass | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | #root.args[0],#a0,#p0 |
caches | root对象 | 当前方法调用使用的缓存列表 如 如@Cacheable(value={"cache1", "cache2"})),则有两个cache | #root.caches[0].name |
Argument Name | 执行上下文evaluator context | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id 可以直接 #参数名 ,也可以使用 #p0或#a0 的形式,0代表参数的索引; | #artsian.id #a0 #p0 |
result | 执行上下文evaluator context | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
网友评论