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;
@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;
}
@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;
}
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;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
public @interface RedisCache {
String prefix() default "";
String key() default "";
int expireTime() default 1800;
TimeUnit unit() default TimeUnit.SECONDS;
Class clazz() default Object.class;
}
import java.lang.annotation.*;
@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;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
public @interface RedisCacheDel {
String prefix() default "";
String key() default "";
}
import java.lang.annotation.*;
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisCacheParam {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
@Documented
public @interface RedisDel {
String key() default "";
}