import com.google.common.collect.Maps;
import com.lvym.support.annotation.LvymCache;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
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.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
/**
* @desc
* @since 2024/9/29
* <p>
* 版本不一样 执行顺序不同
* <p>
* 5.1.5
* : 环绕通知前置拼接缓存key: user:1
* : 前置通知参数 1
* : 环绕通知后置返回查询DB返回
* : 后置通知>>>
* : 返回结果通知查询DB返回
* <p>
* <p>
* 环绕通知没有捕获异常:
* 环绕通知前置拼接缓存key: user:2
* : 前置通知参数 2
* : 后置通知>>>
* : 异常通知方法
* <p>
* <p>
* 6.0.4
* <p>
* 环绕通知前置拼接缓存key: user:1
* : 前置通知参数 1
* : 返回结果通知查询DB返回
* : 后置通知>>>
* : 环绕通知后置返回查询DB返回
* <p>
* <p>
* 环绕通知前置拼接缓存key: user:2
* : 前置通知参数 2
* : 异常通知方法
* : 后置通知>>>
*/
@Component
@Aspect
@Slf4j
public class UserAspect {
private final Map<String, Object> tempCache = Maps.newConcurrentMap();
@Pointcut("@annotation(com.lvym.support.annotation.LvymCache)")
public void pointCut() {
}
/***
* 前置通知
*/
@Before("pointCut()")
public void deBefore(JoinPoint joinPoint) {
log.info("前置通知参数 {}", joinPoint.getArgs());
final Signature signature = joinPoint.getSignature();
//log.info("接口名称 name {}", signature.getName());
//log.info("类名 declaringTypeName {}", signature.getDeclaringTypeName());
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
assert attributes != null; //只做显示,没有实际作用
HttpServletRequest request = attributes.getRequest();
//log.info("接口地址 {}", request.getRequestURL().toString());
//log.info("接口请求方式 {}", request.getMethod());
}
/***
* 后置通知
*/
@After("pointCut()")
public void after(JoinPoint joinPoint) {
log.info("后置通知>>>");
}
/***
* 返回结果通知
*/
@AfterReturning(returning = "o", pointcut = "pointCut()")
public void afterReturning(Object o) {
log.info("返回结果通知{}", o);
}
/***
* 异常通知
*/
@AfterThrowing(throwing = "e", pointcut = "pointCut()")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
log.info("异常通知方法 {},异常{}", joinPoint, e.getMessage());
}
//@Around("execution(* com.lvym.support.service.impl.UserServiceImpl.*())")
@Around("pointCut()")
public Object methodExporter(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
Method method = signature.getMethod();
LvymCache lvymCacheAnnotation = method.getAnnotation(LvymCache.class);
String cacheType = lvymCacheAnnotation.cacheType();
String matchValue = lvymCacheAnnotation.matchValue();
String keyPrefix = lvymCacheAnnotation.keyPrefix();
//SpringEL 解析器
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(matchValue);
EvaluationContext context = new StandardEvaluationContext();
//获得方法里面的形参个数
Object[] args = proceedingJoinPoint.getArgs();
//以第一参数为准
if (isPrimitive(args[0])) {
//非对象
DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);
for (int i = 0; i < Objects.requireNonNull(parameterNames).length; i++) {
context.setVariable(parameterNames[i], args[i].toString());
}
} else {
for (Object arg : args) {
//获取对象的Class对象
Class<?> aClass = arg.getClass();
//获取Class对象中的所有字段
Field[] declaredFields = aClass.getDeclaredFields();
//遍历字段
for (Field field : declaredFields) {
//参数名
String name = field.getName();
//设置字段的可访问性
field.setAccessible(true);
//参数值
Object obj = field.get(arg);
context.setVariable(name, obj);
}
}
}
//拼接redis的最终key形式
String key = keyPrefix + ":" + expression.getValue(context);
log.info("环绕通知前置拼接缓存key: {}", key);
Object object = tempCache.get(key);
if (Objects.nonNull(object)) {
return object;
} else {
Object proceed = proceedingJoinPoint.proceed();
if (Objects.nonNull(proceed)) {
tempCache.put(key, proceed);
}
log.info("环绕通知后置返回{}", proceed);
return proceed;
}
}
public static boolean isPrimitive(Object param) {
return param instanceof Integer ||
param instanceof Float ||
param instanceof Double ||
param instanceof Long ||
param instanceof Boolean ||
param instanceof Character ||
param instanceof Byte ||
param instanceof Short;
}
}
@SpringBootApplication
//@EnableAspectJAutoProxy spring-boot-starter-web--->spring-boot-autoconfigure 会自动装配
public class LvymSupportApplication {
public static void main(String[] args) {
SpringApplication.run(LvymSupportApplication.class, args);
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>