JSR107
Java Caching 定義了5個核心接口 分別是 CachingProvider拍皮,CachManager、Cache跑杭、Entry铆帽、Expiry。
- CachingProvider 定義了創(chuàng)建德谅、配置锄贼、獲取、管理和控制多個CacheManager女阀。一個應用可以在運行期間訪問多CachingProvider宅荤。
- CacheManger 定義了創(chuàng)建、配置浸策、獲取冯键、管理和控制多個唯一命名的Cache,這些Cache 存在于CacheManager 的上下文中庸汗,一個CacheManager 僅被一個CacheingProvider 所擁有惫确。
- Cache 是一個類似Map的數(shù)據(jù)結構并臨時存儲以key 為索引的值。一個Cache 僅被一個CacheManager 所擁有
- Entry 是一個存儲在Cache中的key-value對
- Expiry 每一個存儲在Cache中的條目有一個定義的有效期蚯舱。一旦超過這個有效期改化,條目變?yōu)檫^期狀態(tài)。一旦過期枉昏,條目將不可訪問陈肛、更新、和刪除兄裂。緩存有效期可以通過 ExpiryPolicy 設置句旱。
開發(fā)中使用JSR-107需要導入包:一般不直接使用JSR-107開發(fā),因為JSR-107僅僅定義了接口晰奖,而沒有實現(xiàn)
<!-- 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 注解 簡化我們開發(fā)
- org.springframework.cache.Cache
- org.springframework.cache.CacheManager
- Cache接口為緩存的組件規(guī)范定義谈撒,包含緩存的各種操作集合
-
Cache接口下Spring提供了各種xxxCache的實現(xiàn);如RedisCache匾南、EhCacheCache啃匿、ConcurrentMapCache等;
image.png
執(zhí)行原理:每次調(diào)用需要緩存功能的方法時蛆楞,Spring會檢查指定參數(shù)的目標方法是否已經(jīng)被調(diào)用過溯乒,如果有直接從緩存中獲取方法調(diào)用后的結果,如果沒有就調(diào)用方法并緩存結果返回給用戶臊岸。下次調(diào)用直接從緩存中獲取橙数。
使用Spring緩存抽象時我們需要關注以下兩點:
- 確定方法需要被緩存以及他們都緩存策略
- 從緩存中讀取之前都緩存存儲都數(shù)據(jù)
Spring 緩存開發(fā)重要概念
Spring 緩存開發(fā)相關概念 | 作用功能 |
---|---|
Cache | 緩存接口,定義緩存操作帅戒。實現(xiàn)有 如RedisCache灯帮、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 緩存管理器逻住,管理各種緩存(Cache)組件 |
@Cacheable | 主要針對方法配置钟哥,能夠根據(jù)方法都請求參數(shù)對其結果進行緩存 |
@CacheEvict | 清空緩存 |
@CachePut | 保證方法被調(diào)用,又希望結果被緩存 |
@EnableCaching | 開啟基于注解的緩存 |
KeyGenerator | 緩存數(shù)據(jù)時Key生成策略 |
serilalize | 緩存數(shù)據(jù)時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 環(huán)境(略)
- 導入相關 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:緩存數(shù)據(jù)的 key,默認是使用方法的參數(shù)的值扒秸。
比如:使用方法名字getStu和方法參數(shù)拼接 key:getStu[1]播演,編寫SpEl:#id冀瓦; 參數(shù)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模式下可以看下如下結果:
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 優(yōu)先
sync: 是否使用異步模式默認是false 如果開啟為true 則unless 屬性就不能得到支持
可以獲取 到結果進行判斷 unless = “#result == null”
@CachePut
既調(diào)用方法又更新緩存。修改了數(shù)據(jù)庫某個數(shù)據(jù)洲炊,同時更新緩存
- 先調(diào)用目標方法
- 將目標方法的結果緩存起來
@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"感局,因為先調(diào)用方法然后把結果緩存起來,所以可以拿到結果取出id
注意: 一定要指定key 不然會按默認值方法的參數(shù) 新生成一個 k暂衡,并把結果緩存
@CacheEvict
allEntries 默認 false 是否清空所有緩存
beforeInvocation 默認 false 緩存清除操作在方法之后執(zhí)行询微,如果方法異常,則緩存不會清除
@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對象 | 當前被調(diào)用的方法名 | #root.methodname |
method | root對象 | 當前被調(diào)用的方法 | #root.method.name |
target | root對象 | 當前被調(diào)用的目標對象實例 | #root.target |
targetClass | root對象 | 當前被調(diào)用的目標對象的類 | #root.targetClass |
args | root對象 | 當前被調(diào)用的方法的參數(shù)列表 | #root.args[0]隧膘,#a0代态,#p0 |
caches | root對象 | 當前方法調(diào)用使用的緩存列表 如 如@Cacheable(value={"cache1", "cache2"})),則有兩個cache | #root.caches[0].name |
Argument Name | 執(zhí)行上下文evaluator context | 當前被調(diào)用的方法的參數(shù)疹吃,如findArtisan(Artisan artisan),可以通過#artsian.id 可以直接 #參數(shù)名 蹦疑,也可以使用 #p0或#a0 的形式,0代表參數(shù)的索引萨驶; | #artsian.id #a0 #p0 |
result | 執(zhí)行上下文evaluator context | 方法執(zhí)行后的返回值(僅當方法執(zhí)行后的判斷有效歉摧,如 unless cacheEvict的beforeInvocation=false) | #result |