SpringBoot学习之整合AOP
基于SpringBoot整合Aop记录日志
-
SpringBoot各版本参考文档
https://docs.spring.io/spring-boot/docs/
-
查找引入依赖
-
引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
-
切面类
package link.lycreate.springbooteasyexceldemo.aspect; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Arrays; /** * @ClassName LogAspect * @Description TODO 日志切面类$ * @Author charlesYan * @Date 2020/10/9 12:53 * @Version 1.0 **/ @Aspect // 声明是一个切面组件 @Component // 加入到IOC容器 @Slf4j // 等同于 private final Logger logger = LoggerFactory.getLogger(XXX.class); public class LogAspect { /** * @Author charlesYan * @Description // 指定切入点表达式,拦截那些方法,即为哪些类生成代理对象 * 第一*表示匹配任何返回值的方法 * 第二*表示匹配controller包下的所有类 * 第三*表示匹配类下的所有方法 * ..表示任何个数参数,和如何类型的参数 **/ //@Pointcut("execution(* link.lycreate.springbooteasyexceldemo.controller.*.*(..))") @Pointcut("@annotation(link.lycreate.springbooteasyexceldemo.aspect.LogFilter)") //在所有标记指定注解的方法上拦截 public void logPointCut(){} /** * @Author charlesYan * @Description //前置通知:在目标方法执行前调用 **/ @Before("logPointCut()") public void before(JoinPoint joinPoint){ System.out.println("---------------Before Begin CurrentTime = " + System.currentTimeMillis()); /*获取当前请求的HttpServletRequest*/ RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest(); log.info("URL-->"+request.getRequestURL().toString()); log.info("IP-->"+request.getRemoteAddr()); log.info("HTTP_Method-->"+request.getMethod()); log.info("Request_args-->"+ Arrays.toString(joinPoint.getArgs())); System.out.println("---------------Before End CurrentTime = " + System.currentTimeMillis()); } /** * @Author charlesYan * @Description //返回通知 在目标方法执行后调用,若目标方法出现异常,则不执行 **/ @AfterReturning(value = "logPointCut()",returning = "obj") public void afterReturning(JoinPoint joinPoint,Object obj){ System.out.println("---------------AfterReturn CurrentTime = " + System.currentTimeMillis()); } /** * @Author charlesYan * @Description //后置通知:无论目标方法在执行过程中是否出现异常都会在它之后调用 **/ @After("logPointCut()") public void after(JoinPoint joinPoint){ System.out.println("---------------After CurrentTime = " + System.currentTimeMillis()); } /** * @Author charlesYan * @Description //异常通知:目标方法抛出异常时执行 **/ @AfterThrowing(value = "logPointCut()", throwing = "ex") public void afterThrowing(JoinPoint joinPoint,Exception ex){ System.out.println("---------------AfterThrowing CurrentTime = " + System.currentTimeMillis()); } /** * @Author charlesYan * @Description //环绕通知:是前面四个通知的结合体 * 需要在方法之前执行,可以写在joinPoint.procedd();之前 * 需要在方法之后执行,可以写在joinPoint.procedd();之后 **/ @Around("logPointCut()") public void around(ProceedingJoinPoint joinPoint) throws Throwable { // 获取目标方法的名称 String methodName = joinPoint.getSignature().getName(); // 获取方法传入参数 Object[] params = joinPoint.getArgs(); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // 获取方法上LogFilter注解 LogFilter logFilter = method.getAnnotation(LogFilter.class); String value = logFilter.value() ; log.info("模块描述:"+value); System.out.println("---------------Around Before CurrentTime = " + System.currentTimeMillis() + " method name:" + methodName + " args:" + params[0]); // 执行源方法 joinPoint.proceed(); System.out.println("---------------Around After CurrentTime = " + System.currentTimeMillis() + " method name:" + methodName + " args:" + params[0]); } }
-
自定义注解
/** * @ClassName LogFilter * @Description TODO 自定义日志注解类$ * @Author charlesYan * @Date 2020/10/11 17:59 * @Version 1.0 **/ @Target(ElementType.METHOD)//Target注解决定LogFilter注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分 @Retention(RetentionPolicy.RUNTIME)//Retention注解括号中的"RetentionPolicy.RUNTIME"意思是让LogFilter这个注解的生命周期一直程序运行时都存在 @Documented public @interface LogFilter { String value() default ""; }
-
请求方法
@LogFilter("保存请求日志") @RequestMapping(path = "/saveRequestLog",method = RequestMethod.POST) public String saveRequestLog(@RequestParam("name")String name){ return "请求成功:" + name; }
-
测试方式
报错信息
方法参数未声明
-
报错信息
Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut
-
截图信息
-
错误代码
/** * @Author charlesYan * @Description //返回通知 在目标方法执行后调用,若目标方法出现异常,则不执行 **/ @AfterReturning("logPointCut()") public void afterReturning(JoinPoint joinPoint,Object obj){ System.out.println("---------------AfterReturn CurrentTime = " + System.currentTimeMillis()); }
-
正确代码
/** * @Author charlesYan * @Description //返回通知 在目标方法执行后调用,若目标方法出现异常,则不执行 **/ @AfterReturning(value = "logPointCut()", returning = "obj") public void afterReturning(JoinPoint joinPoint,Object obj){ System.out.println("---------------AfterReturn CurrentTime = " + System.currentTimeMillis()); }
-
原因分析
新增的方法参数未赋值
总结
Aop切面通知执行顺序
- 例图
通知注解中的value属性补充
-
自定义注解
//注解实体类 package com.trip.demo; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) public @interface SMSAndMailSender { /*短信模板String格式化串*/ String value() default ""; String smsContent() default ""; String mailContent() default ""; /*是否激活发送功能*/ boolean isActive() default true; /*主题*/ String subject() default ""; }
-
切面类中的切面方法
/** * 在所有标记了@SMSAndMailSender的方法中切入 * @param joinPoint * @param obj */ @AfterReturning(value="@annotation(com.trip.demo.SMSAndMailSender)", returning="obj") public void afterReturning(JoinPoint joinPoint,Object obj){ System.out.println("---------------AfterReturn CurrentTime = " + System.currentTimeMillis()); }
参考链接
-
SpringBoot整合Aop
https://www.cnblogs.com/fernfei/p/12092510.html
-
SpringBoot整合aop
https://www.cnblogs.com/myitnews/p/11848159.html
-
SpringBoot 通过自定义注解实现AOP切面编程实例
https://www.cnblogs.com/lingyejun/p/9941350.html
-
SpringBoot2.0 基础案例(11):配置AOP切面编程,解决日志记录业务
https://my.oschina.net/cicadasmile/blog/3073069
-
浅谈Spring AOP 面向切面编程 最通俗易懂的画图理解AOP、AOP通知执行顺序~
https://www.cnblogs.com/ChromeT/p/11823735.html
-
@interface 注解详解
https://blog.csdn.net/u010882691/article/details/82427520