关于 Spring Boot 引用 AOP 请参考前文:【HAVENT原创】使用 Spring Boot 的 AOP 全局记录执行时间日志
接下来我们要使用自定义的注解:
1. 首先定义一个注解接口对象
package com.havent.demo.aop.target;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HHWebLog {
String value() default "自动日志开始...";
String level() default "info";
String path() default "";
}
2. 在 HHWebLogAspect.java 中创建新的切面点并关联注解接口
@Pointcut(value = "@annotation(com.havent.demo.aop.target.HHWebLog)")
public void webLog(){}
3. 创建 前置通知 注入方法
@Before(pointcut = "webLog()")
public void deBefore(JoinPoint joinPoint) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("HH URL : " + request.getRequestURL().toString());
System.out.println("HH HTTP_METHOD : " + request.getMethod());
System.out.println("HH IP : " + request.getRemoteAddr());
System.out.println("HH CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
System.out.println("HH ARGS : " + Arrays.toString(joinPoint.getArgs()));
//logger.trace("");
}
如需获取注解中的参数,则代码如下
@Before("@annotation(hhWebLog)")
public void deBefore(JoinPoint joinPoint, HHWebLog hhWebLog) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("HH URL : " + request.getRequestURL().toString());
System.out.println("HH HTTP_METHOD : " + request.getMethod());
System.out.println("HH IP : " + request.getRemoteAddr());
System.out.println("HH CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
System.out.println("HH ARGS : " + Arrays.toString(joinPoint.getArgs()));
System.out.println("HH targe:" + hhWebLog.path());
//logger.trace("");
}
4. 创建 返回通知 注入方法
@AfterReturning(pointcut = "webLog()", returning = "result")
public void doAfterReturning(Object result) throws Throwable {
// 处理完请求,返回内容
System.out.println("HH 方法的返回值 : " + result);
//logger.trace("");
}
5. 创建 后置异常 注入方法
//后置异常通知
@AfterThrowing("webLog()")
public void throwss(JoinPoint jp){
System.out.println("HH 方法异常时执行.....");
//logger.trace("");
}
6. 创建 后置通知 注入方法
//后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
@After("webLog()")
public void after(JoinPoint jp){
System.out.println("HH 方法最后执行.....");
}
完整 HHWebLogAspect.java 如下
package com.havent.demo.aop.aspect;
import com.havent.demo.aop.target.HHWebLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
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.util.Arrays;
@Aspect
@Component
public class HHWebLogAspect {
@Pointcut(value = "@annotation(com.havent.demo.aop.target.HHWebLog)")
public void webLog(){}
@Before("@annotation(hhWebLog)")
public void deBefore(JoinPoint joinPoint, HHWebLog hhWebLog) throws Throwable {
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录下请求内容
System.out.println("HH URL : " + request.getRequestURL().toString());
System.out.println("HH HTTP_METHOD : " + request.getMethod());
System.out.println("HH IP : " + request.getRemoteAddr());
System.out.println("HH CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
System.out.println("HH ARGS : " + Arrays.toString(joinPoint.getArgs()));
System.out.println("HH targe:" + hhWebLog.path());
//logger.trace("");
}
@AfterReturning(pointcut = "webLog()", returning = "result")
public void doAfterReturning(Object result) throws Throwable {
// 处理完请求,返回内容
System.out.println("HH 方法的返回值 : " + result);
//logger.trace("");
}
//后置异常通知
@AfterThrowing("webLog()")
public void throwss(JoinPoint jp){
System.out.println("HH 方法异常时执行.....");
//logger.trace("");
}
//后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
@After("webLog()")
public void after(JoinPoint jp){
System.out.println("HH 方法最后执行.....");
}
}
7. 外部注册注解代码
package com.havent.demo.controller;
import com.havent.demo.aop.target.HHWebLog;
import com.havent.demo.logger.service.KafkaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@HHWebLog(level = "error", path = "/test")
@RequestMapping("/test")
public String test() {
return "test";
}
}
访问 http://localhost:8081/test 效果如下:
那么如果有多个 AOP 拦截器,他们的执行顺序是什么样子的呢?
Spring AOP 就是面向切面编程,什么是切面,画一个图来理解下:
由此得出:spring aop 就是一个同心圆,要执行的方法为圆心,最外层的 order 最小。从最外层按照 AOP1、AOP2 的顺序依次执行 doAround 方法,doBefore 方法。然后执行 method 方法,最后按照 AOP2、AOP1 的顺序依次执行 doAfter、doAfterReturn 方法。也就是说对多个 AOP 来说,先 before 的,一定后 after。
如果我们要在同一个方法事务提交后执行自己的 AOP,那么把事务的 AOP order 设置为2,自己的 AOP order 设置为1,然后在 doAfterReturn 里边处理自己的业务逻辑。
同事执行前篇文章的 AOP 和本文中的自定义注解 AOP 的效果如下: