基于SpringBoot整合Aop记录日志
    • SpringBoot各版本参考文档

      https://docs.spring.io/spring-boot/docs/

    • 查找引入依赖
      SpringBoot学习之整合AOP_SpringBoot

    •  

    • 引入依赖

       


    •   <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;
  }



  • 测试方式SpringBoot学习之整合AOP_Aop_02

报错信息

方法参数未声明

  • 报错信息

     

  •   Caused by: java.lang.IllegalArgumentException: error at ::0 formal unbound in pointcut



  • 截图信息
    SpringBoot学习之整合AOP_Aop_03

  • 错误代码

     

  /**
  * @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切面通知执行顺序

  • 例图SpringBoot学习之整合AOP_Aop_04

通知注解中的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());

  }