RedisCacheAspect 缓存注解

376 阅读2分钟
import com.alibaba.fastjson.JSON;
import com.megvii.performance.common.annotation.*;
import com.megvii.performance.common.utils.PubUtil;
import com.megvii.performance.common.utils.RedisUtil;
import com.megvii.performance.common.utils.SystemUtil;
import com.megvii.performance.pojo.entity.sys.OperateLogDO;
import com.megvii.performance.pojo.entity.sys.SysUserDO;
import com.megvii.performance.service.sys.OperateLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;

/***
 * @Author: sunkui
 * @Date: 2019/10/17
 * @Time: 10:55
 * @Description:
 ***/
@Aspect
@Component
@Slf4j
public class RedisCacheAspect {

    private RedisUtil redisUtil;

    private OperateLogService logService;

    @Autowired
    public RedisCacheAspect(RedisUtil redisUtil, OperateLogService logService){
        this.redisUtil = redisUtil;
        this.logService = logService;
    }

    /***
     * 切入点:方法上带有@RedisCache@RedisDel注解的方法
     */
    @Pointcut(value = "@annotation(com.megvii.performance.common.annotation.RedisCache) " +
            "|| @annotation(com.megvii.performance.common.annotation.RedisCacheDel)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object redisCache(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
        //获取注释
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        Object[] args = pjp.getArgs();
        Object keyValue = null;
        Parameter[] parameters = method.getParameters();
        breakLoop:
        for (int i = 0; i < parameters.length; i++) {
            Annotation[] annotations = parameters[i].getAnnotations();
            if (null != annotations){
                for (Annotation annotation:annotations) {
                    if (annotation instanceof RedisCacheParam) {
                        keyValue = args[i];
                        break breakLoop;
                    }else if (annotation instanceof RedisCacheBody){
                        Field[]  fields = args[i].getClass().getDeclaredFields();
                        for (int j = 0; j < fields.length; j++) {
                            if(fields[j].isAnnotationPresent(RedisCacheParam.class)){
                                keyValue = PubUtil.getFieldValueByName(fields[j].getName(), args[i]);
                                break breakLoop;
                            }
                        }
                    }else{
                        return pjp.proceed();
                    }
                }
            }
        }
        String redisResult;
        String key = String.valueOf(keyValue);
        //得到被代理的方法上的注解
        Object result = null;
        //判断方法上是否有加入缓存的注解
        if (method.isAnnotationPresent(RedisCache.class)){
            String prefix = method.getAnnotation(RedisCache.class).prefix();
            int expireTime = method.getAnnotation(RedisCache.class).expireTime();
            if (StringUtils.isNotEmpty(prefix)){
                key = prefix+":"+key;
            }
            log.info("生成的key[{}]",key);
            if(!redisUtil.hasKey(key)) {
                log.info("key={}缓存未命中",key);
                //缓存不存在,则调用原方法,并将结果放入缓存中
                //开始时间
                long startTime = System.currentTimeMillis();
                result = pjp.proceed(args);
                redisResult = JSON.toJSONString(result);
                //结束时间
                long endTime = System.currentTimeMillis();
                int timeConsuming = Math.toIntExact(endTime - startTime);
                saveLog(pjp, redisResult, timeConsuming);
                if (expireTime==-1){
                    redisUtil.set(key,redisResult);
                }else {
                    redisUtil.setWithExpireTime(key,redisResult,expireTime);
                }
            } else{
                //缓存命中
                log.info("key={}缓存命中",key);
                redisResult = redisUtil.get(key);
                //得到被代理方法的返回值类型
                Class returnType = method.getReturnType();
                Class returnClass = method.getAnnotation(RedisCache.class).clazz();
                if ( Collection.class.isAssignableFrom(returnType)){
                    result = JSON.parseArray(redisResult,returnClass);
                }else{
                    result = JSON.parseObject(redisResult,returnType);
                }
            }
        }
        //判断方法是否有删除缓存的注解
        else if (method.isAnnotationPresent(RedisCacheDel.class)){
            String prefix = method.getAnnotation(RedisCacheDel.class).prefix();
            if (StringUtils.isNotEmpty(prefix)){
                key = prefix+":"+key;
            }
            result = pjp.proceed(args);
            log.info("删除的key[{}]",key);
            redisUtil.delete(key);
        }else {
            //执行方法。返回
            result = pjp.proceed(args);
        }
        return result;
    }

    /***
     * 保存日志
     * @param joinPoint
     * @param resultData 方法执行结果
     * @param timeConsuming 耗费时间
     */
    private void saveLog(ProceedingJoinPoint joinPoint, Object resultData, int timeConsuming) throws Throwable{
        try {
            //获取参数数组
            Object[] args = joinPoint.getArgs();
            //获取操作用户信息
            SysUserDO user = (SysUserDO)SecurityUtils.getSubject().getPrincipal();
            //获取请求信息
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

            OperateLogDO operateLog = new OperateLogDO();
            operateLog.setOperateDate(new Date());
            operateLog.setOperateUserId(user.getUserId());
            operateLog.setRequestIp(SystemUtil.getRealIP(request));
            operateLog.setRequestUrl(request.getRequestURL().toString());
            operateLog.setRequestParams(Arrays.toString(args));
            operateLog.setResponseResult(String.valueOf(resultData));
            operateLog.setTimeConsuming(timeConsuming);
            logService.save(operateLog);
        }catch (Throwable throwable) {
            log.error(throwable.getMessage());
        }
    }

}
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/***
 * @Author: sunkui
 * @Date: 2019/10/17
 * @Time: 11:27
 * @Description: 自定义redis查询注解
 ***/

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
public @interface RedisCache {
    /***
     * 存储前缀
     * @return
     */
    String prefix() default "";

    /***
     * 要查询的key,默认是查询条件的第一个参数
     * @return
     */
    String key() default "";

    /****
     * 默认 1800秒
     * @return
     */
    int expireTime() default 1800;

    /***
     * 默认值是以秒为单位
     * @return
     */
    TimeUnit unit() default TimeUnit.SECONDS;

    /***
     * 返回值类型,若返回值为集合则需设置集合泛型
     * @return
     */
    Class clazz() default Object.class;
}


import java.lang.annotation.*;

/***
 * @Author: sunkui
 * @Date: 2019/12/12
 * @Time: 14:22
 * @Description: 缓存的方法中判断key的唯一参数对象
 ***/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisCacheBody {
}
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

/***
 * @Author: sunkui
 * @Date: 2019/10/17
 * @Time: 11:27
 * @Description: 自定义redis查询注解
 ***/

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
public @interface RedisCacheDel {
    /***
     * 存储前缀
     * @return
     */
    String prefix() default "";

    /***
     * 要查询的key,默认是查询条件的第一个参数
     * @return
     */
    String key() default "";
}
import java.lang.annotation.*;

/***
 * @Author: sunkui
 * @Date: 2020/3/19
 * @Time: 15:03
 * @Description: 需要缓存的参数名
 ***/

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisCacheParam {
}
/***
 * @Author: sunkui
 * @Date: 2019/10/17
 * @Time: 11:24
 * @Description: 自定义redis删除key注解
 ***/

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
public @interface RedisDel {

    /***
     * key
     * @return
     */
    String key() default "";
}