注解需要的maven坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
首先需要了解execution表达式如何书写
eq:execution(public * com.example.druid.controller..*.*(..))
第一个"*" ---------------- 表示匹配任意返回类型
com.example.druid.controller -------------- controller的存放包名,需要替换
.. --------------- 当前包以及子包
第二个"*" --------------- 所有类
第三个"*" ---------------- 所有方法
(..) ----------------- 所有类型的参数,如(int a,int b);(int a)
①新建类,在类名上分添加以下注解:
@Aspect
@Component
@Slf4j
@Order(1)
@Aspect 和 @Component 是必须的,@Slf4j 用来打印日志,@Order(1) 设置切面优先级
②定义切入点
在新建的类中新建一个方法,方法名任意取,在方法名上分添加注解如下:
@Pointcut("execution(public * com.example.druid.controller..*.*(..))")
//@Pointcut("@annotation(com.example.druid.annotation.Limit)") 也可以这么使用,添加了注解就会调用,括号里面的值是注解的全路径
public void point(){
}
③在切入点开始处切入内容
新建方法,方法名任意,参数是 (JoinPoint joinPoint),然后再在方法上添加注解如下:
@Before("point()") //point换成步骤二中自己取的方法名
public void before(JoinPoint joinPoint){
// 获取请求域
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
// 记录请求内容
log.info("请求开始时间: "+System.currentTimeMillis());
log.info("接口路径: " + request.getRequestURI().toLowerCase());
log.info("请求方式: " + request.getMethod());
log.info("请求者ip: " + request.getRemoteAddr());
log.info("请求方法的类名: " + joinPoint.getSignature().getDeclaringTypeName() );
log.info("请求方法名: "+joinPoint.getSignature().getName());
log.info("传入的参数: " + Arrays.toString(joinPoint.getArgs()));
}
获取该注解所在的方法对象:
Method resolveMethod(JoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
Class<?> targetClass = point.getTarget().getClass();
Method method = getDeclaredMethod(targetClass, signature.getName(), signature.getMethod().getParameterTypes());
if (method == null) {
throw new IllegalStateException("无法解析目标方法: " + signature.getMethod().getName());
}
return method;
}
private Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
try {
return clazz.getDeclaredMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null) {
return getDeclaredMethod(superClass, name, parameterTypes);
}
}
return null;
}
@Before 表示,在方法执行前调用,如果你的 controller 中,有参数校验如(@RequestParam(value = "aa",required = true) String aa),若你未传此参数,不会进入方法中,自然不会调用添加了@Before的方法。
④在切入点结束后切入内容
新建方法,在方法上添加如下注解:
@After("point()") //point换成步骤二中自己的方法名
public void after(){
log.info("方法结束时间: "+System.currentTimeMillis());
}
@After 表示,在方法执行后调用
⑤在切入点return内容之后切入内容
新建方法,在方法上添加如下注解:
@AfterReturning(returning = "result", pointcut = "point()")
public void doAfterReturning(Object result) throws Throwable {
log.info("方法返回值: " + result);
}
@AfterReturning 表示,在方法返回结果后调用 ,在@After之前执行
returning属性:将返回值用value接收,方法可以打印或处理value值
⑥在切入点发生异常后切入内容
新建方法,在方法上添加如下注解:
@AfterThrowing(pointcut = "point()", throwing = "ex")
public void afterReturning(JoinPoint point, Exception ex) {
String methodName = point.getSignature().getName();
List<Object> args = Arrays.asList(point.getArgs());
System.out.println("连接点方法为:" + methodName + ",参数为:" + args + ",异常为:" + ex);
}
@AfterThrowing 表示在切入点发生异常后调用