Springboot自定義註解封裝快取操作
阿新 • • 發佈:2018-11-10
通常,我們為了避免頻繁的查詢訪問資料庫或者第三方介面,會把查詢結果快取到redis或者memcached之類的nosql資料庫中,避免資料庫或者網路開銷過大導致程式效率太低或者雪崩效應,但是程式碼中頻繁的操作快取,會讓程式碼過於冗長,可以通過自定義註解的方式封裝快取的操作,使程式碼更簡潔,話不多說,直接上程式碼:
1.先定義註解@EnableCacheService
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EnableCacheService {
/**
* key字首
*/
String keyPrefix();
/**
* key主體,spel表示,例:#id(取形參中id的值)
*/
String fieldKey();
/**
* 過期時間
*/
int expireTime() default 3600;
TimeUnit timeUnit() default TimeUnit.SECONDS;
CacheOperation cacheOperation();
/**
* 快取操作型別
*/
enum CacheOperation {
QUERY, // 查詢
UPDATE, // 修改
DELETE; // 刪除
}
}
2.切面處理類
/**
* EnableRedisService 註解切面處理
* @author: Iffie
* @date: 2018年9月29日
*/
@Aspect
@Component
@Slf4j
@SuppressWarnings("all")
public class CacheServiceAspect {
@Pointcut("@annotation(com.iffie.core.EnableCacheService)" )
public void dealCacheServiceCut(){}
@Autowired
private RedisTemplate redisTemplate;
@Around(value = "dealCacheServiceCut()")
@SuppressWarnings("all")
public Object dealCacheService(ProceedingJoinPoint point) throws Throwable{
Method method = getMethod(point);
// 獲取註解物件
EnableCacheService cacheServiceAnnotation = method.getAnnotation(EnableCacheService.class);
//所有引數
Object[] args = point.getArgs();
String cacheKey = cacheServiceAnnotation.keyPrefix()+parseKey(cacheServiceAnnotation.fieldKey(), method, args);
log.info("{} enable cache service,cacheKey:{}",point.getSignature(),cacheKey);
CacheOperation cacheOperation = cacheServiceAnnotation.cacheOperation();
if(cacheOperation==CacheOperation.QUERY) {
return processQuery(point, cacheServiceAnnotation, cacheKey);
}
if(cacheOperation==CacheOperation.UPDATE||cacheOperation==CacheOperation.DELETE) {
return processUpdateAndDelete(point, cacheKey);
}
return point.proceed();
}
/**
* 查詢處理
*/
private Object processQuery(ProceedingJoinPoint point, EnableCacheService cacheServiceAnnotation, String cacheKey)
throws Throwable {
if(redisTemplate.hasKey(cacheKey)) {
log.info("{} enable cache service,has cacheKey:{} , return",point.getSignature(),cacheKey);
return redisTemplate.opsForValue().get(cacheKey);
}else {
Object result = null;
try {
return result = point.proceed();
} finally {
redisTemplate.opsForValue().
set(cacheKey, result, cacheServiceAnnotation.expireTime(), cacheServiceAnnotation.timeUnit());
log.info("after {} proceed,save result to cache,redisKey:{},save content:{}",point.getSignature(),cacheKey,result);
}
}
}
/**
*刪除和修改處理
*/
private Object processUpdateAndDelete(ProceedingJoinPoint point, String cacheKey)
throws Throwable {
//通常來講,資料庫update操作後,只需刪除掉原來在快取中的資料,下次查詢時就會重新整理
try {
return point.proceed();
} finally {
redisTemplate.delete(cacheKey);
}
}
private Method getMethod(JoinPoint joinPoint) throws Exception {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
return method;
}
/**
* 獲取redis的key
*/
private String parseKey(String fieldKey,Method method,Object [] args){
//獲取被攔截方法引數名列表(使用Spring支援類庫)
LocalVariableTableParameterNameDiscoverer u =
new LocalVariableTableParameterNameDiscoverer();
String [] paraNameArr=u.getParameterNames(method);
//使用SPEL進行key的解析
ExpressionParser parser = new SpelExpressionParser();
//SPEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
//把方法引數放入SPEL上下文中
for(int i=0;i<paraNameArr.length;i++){
context.setVariable(paraNameArr[i], args[i]);
}
return parser.parseExpression(fieldKey).getValue(context,String.class);
}
}
3.Demo
@EnableCacheService(keyPrefix=Constants.USER_REDIS_KEY_PREFIX,
fieldKey="#id",cacheOperation=CacheOperation.QUERY)
public User expressIssueInfo(String id) {
return userMapper.selectByPrimaryKey(id);
}