引入spring-boot-starter-aop maven依赖
org.springframework.boot
spring-boot-starter-aop
创建日志切面类
@Aspect // 添加Aspect注解
@Component // 注册为SpringBean
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
// 定义该切面的切入点为allCtlMethod(), 需要被切入的方法为: ctl这个包下的所有带@RequestMapping, @PostMapping, @GetMapping的方法
@Pointcut("execution(* com.welldone.calcprogram.ctl..*.*(..)) "
+ "&& "
+ "(@annotation(org.springframework.web.bind.annotation.RequestMapping)"
+ "|| @annotation(org.springframework.web.bind.annotation.PostMapping)"
+ "|| @annotation(org.springframework.web.bind.annotation.GetMapping)"
+ ")"
)
public void allCtlMethod() {}
/** allCtlMethod()指定的目标方法执行前执行before方法
* @param call 切入点,即目标方法
*/
@Before("allCtlMethod()")
public void before(JoinPoint call) {
if (log.isInfoEnabled()) {
String clazzName = call.getTarget().getClass().getName();
String methodName = call.getSignature().getName();
Object[] args = call.getArgs();
StringBuffer sb = new StringBuffer()
.append("[start]").append(clazzName).append(".").append(methodName).append(", args: ");
for (Object arg : args) {
sb.append(arg);
}
log.info(sb.toString());
}
}
/** allCtlMethod()指定的目标方法返回后执行afterReturning方法
returning的值要和Object的参数名obj一致
* @param call 切入点,即目标方法
* @param obj 目标方法返回值
*/
@AfterReturning(pointcut = "allCtlMethod()", returning = "obj")
public void afterReturning(JoinPoint call, Object obj) {
if (log.isInfoEnabled()) {
String clazzName = call.getTarget().getClass().getName();
String methodName = call.getSignature().getName();
String output = obj != null ? obj.toString() : "";
log.info("[end]" + clazzName + "." + methodName + ", returnValue: " + output);
}
}
}
测试
访问Controller中其中一个请求接口,打印日志如下:
2022-12-03 17:29:26.584 INFO 247728 --- [nio-8080-exec-4] c.w.calcprogram.ext.aspect.LogAspect : [start]com.welldone.calcprogram.ctl.CalcController.calc, args: 1997-04-14 00:00:00
2022-12-03 17:29:26.584 INFO 247728 --- [nio-8080-exec-4] c.w.calcprogram.ctl.CalcController : 执行CalcController.calc
2022-12-03 17:29:26.584 INFO 247728 --- [nio-8080-exec-4] c.w.calcprogram.ext.aspect.LogAspect : [end]com.welldone.calcprogram.ctl.CalcController.calc, returnValue: 1997-04-14 00:00:00
@Before, @After, @Around, @AfterReturning, @AfterThrowing区别
@Before是在所拦截方法执行之前执行一段逻辑。
@After 是在所拦截方法执行之后执行一段逻辑(不论是正常返回还是异常退出)。
@Around是可以同时在所拦截方法的前后执行一段逻辑。
@AfterReturning 正常完成后执行的通知,不包括抛出异常的情况。
@AfterThrowing 抛出异常后通知,在方法抛出异常退出时执行的通知。
@Before, @After, @Around, @AfterReturning, @AfterThrowing执行顺序:
正常返回时,走AfterReturning
@Around前置逻辑 -> @Before -> 执行目标方法 -> @Around后置逻辑 -> @After -> @AfterReturning
抛异常时,走AfterThrowing
@Around前置逻辑 -> @Before -> 执行目标方法 -> @Around后置逻辑 -> @After -> @AfterThrowing
@Pointcut 切入点,指的是需要被增强的目标方法, @Pointcut中的execution表达式为切入点表达式。