1. 程式人生 > 其它 >Spring 中快取註解的使用

Spring 中快取註解的使用

CacheAnnotation

在 SpringBoot 中使用快取註解, 其原理是藉助於 AOP (動態代理) 對目標方法進行增強。

@CacheConfig

抽取快取的公共配置, 只能在類上宣告。方法上的同名屬性會覆蓋類上的配置。

@Cacheable

在呼叫方法之前,首先在快取中查詢方法的返回值,如果存在,直接返回快取中的值,否則執行該方法,並將返回值儲存到快取中

value/cacheNames: 該屬性值必須提供,用於指定快取組的名字,可以指定多個快取組名稱

key: 快取資料使用的 key, 一條資料的名稱由快取組和 key 所組成,不指定 key 則預設是使用方法引數的值,該屬性值支援SpEL表示式 (詳情可查閱原始碼註釋)

keyGenerator: 可為空, 指定 key 名稱生成器, 當 key 屬性未設定時根據生成器規則生成快取的 key

cacheManager: 可為空, 指定快取管理器, 用於建立 cacheResolver

cacheResolver: 可為空, 指定獲取解析器, 不能與 cacheManager 同時設定

condition:可為空, 指定符合條件的情況下才啟用快取, 當判斷結果為 false 時該快取註解將不啟用, 支援SpEL表示式

unless: 可為空, 指定是否不儲存快取, 當判斷結果為 true 時將放棄對返回值進行快取

sync: 預設值為 false, 指定是否以同步的方式操作目標快取

@CachePut

執行目標方法,並將返回值進行快取

@CacheEvict

刪除指定的快取資料

allEntries: 預設值為 false, 是否刪除快取組中所有的 key, 為 true 時, 註解中將不能設定 key 屬性

beforeInvocation: 預設值為 false, 是否在方法執行器刪除快取資料, 當為 true 時無論方法是否成功執行, 都會刪除快取資料

@CacheIng

分組註解, 可以在該註解中宣告 cacheable, CachePut 和 CacheEvict 屬性

SpEL

root.method

引用目標方法物件

root.target

引用目標執行物件

root.caches

引用受影響的快取

root.methodName

引用目標方法名稱

root.targetClass

引用目標執行物件類名

root.args[1], #p1, #a1

引用引數列表中的第 2 個引數, 或者使用 #引數名

result

引用返回值物件

註解功能實現

以下程式碼可以實現與 @Cacheable 相同效果的快取功能 (需要先配置 Redis 資料來源), 同理 @CachePut, @CacheEvict 原理也大致相同。

package com.xtyuns.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Aspect
@Component
public class CacheAspect {
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    // 使用 Jackson 序列化快取鍵值屬性
    @PostConstruct
    private void init() {
        this.redisTemplate.setKeySerializer(new GenericJackson2JsonRedisSerializer());
        this.redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    }

    @Around("execution(* com.xtyuns.service.impl.UserServiceImpl.selectByPrimaryKey(..))")
    public Object cacheGetUser(ProceedingJoinPoint pjp) throws Throwable {
        Object id = pjp.getArgs()[0];
        Object o = this.redisTemplate.opsForValue().get(id);
        // 如果快取中存在資料, 則直接返回
        if (null != o) return o;
        
        // 未命中快取, 執行資料庫查詢並將資料放入快取
        Object result = pjp.proceed();
        this.redisTemplate.opsForValue().set(id, result);
        return result;
    }
}