SpringCache

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 設置句旱。
image.png

開發(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緩存抽象時我們需要關注以下兩點:

  1. 確定方法需要被緩存以及他們都緩存策略
  2. 從緩存中讀取之前都緩存存儲都數(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)境(略)

  1. 導入相關 pom文件
  <!--    Spring 緩存    -->
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-cache</artifactId>
   </dependency>
  1. 開啟基于緩存的注解 @EnableCaching

  2. 標注緩存注解 @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模式下可以看下如下結果:


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 優(yōu)先

sync: 是否使用異步模式默認是false 如果開啟為true 則unless 屬性就不能得到支持

可以獲取 到結果進行判斷 unless = “#result == null”

@CachePut

既調(diào)用方法又更新緩存。修改了數(shù)據(jù)庫某個數(shù)據(jù)洲炊,同時更新緩存

  1. 先調(diào)用目標方法
  2. 將目標方法的結果緩存起來
    @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
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市腔呜,隨后出現(xiàn)的幾起案子叁温,更是在濱河造成了極大的恐慌,老刑警劉巖核畴,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件膝但,死亡現(xiàn)場離奇詭異,居然都是意外死亡谤草,警方通過查閱死者的電腦和手機跟束,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來丑孩,“玉大人冀宴,你說我怎么就攤上這事紊服√直耍” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵茫陆,是天一觀的道長。 經(jīng)常有香客問我逃延,道長览妖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任真友,我火速辦了婚禮黄痪,結果婚禮上,老公的妹妹穿的比我還像新娘盔然。我一直安慰自己,他們只是感情好是嗜,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布愈案。 她就那樣靜靜地躺著,像睡著了一般鹅搪。 火紅的嫁衣襯著肌膚如雪站绪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天丽柿,我揣著相機與錄音恢准,去河邊找鬼。 笑死甫题,一個胖子當著我的面吹牛馁筐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坠非,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼敏沉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炎码?” 一聲冷哼從身側(cè)響起盟迟,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎潦闲,沒想到半個月后攒菠,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡歉闰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年辖众,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片新娜。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡赵辕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出概龄,到底是詐尸還是另有隱情还惠,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布私杜,位于F島的核電站蚕键,受9級特大地震影響救欧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锣光,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一笆怠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧誊爹,春花似錦蹬刷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搂漠,卻和暖如春迂卢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桐汤。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工而克, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人怔毛。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓员萍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親馆截。 傳聞我的和親對象是個殘疾皇子充活,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354