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;
}
}