1. 程式人生 > >Springboot自定義註解封裝快取操作

Springboot自定義註解封裝快取操作

通常,我們為了避免頻繁的查詢訪問資料庫或者第三方介面,會把查詢結果快取到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);
	}