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{
    	try {
    		Method method = getMethod(point);
        	// 获取注解对象
        	EnableCacheService cacheServiceAnnotation = method.getAnnotation(EnableCacheService.class);
        	//所有参数
        	Object[] args = point.getArgs();
        	String fieldKey = parseKey(cacheServiceAnnotation.fieldKey(), method, args);
        	if(StringUtils.isEmpty(fieldKey)) {
        		return point.proceed();
        	}
            String cacheKey = cacheServiceAnnotation.keyPrefix()+fieldKey;
            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);
            }
		} catch (Exception e) {
			log.error("dealCacheService error,JoinPoint:{}",point.getSignature(),e);
		}
    	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);
	}

你可能感兴趣的:(自定义注解)