zoukankan      html  css  js  c++  java
  • SpringBoot中应用SpringAOP实现记录日志功能

    SpringBoot中应用SpringAOP实现记录日志功能

     

    1.背景

    需要把所有访问controller的请求方法、请求参数、返回值类型都保存到数据库表中,可以利用SpringAOP切面编程来实现。

    2.实现步骤

    • 首先添加依赖,只要引入SpringAOP相关的jar包依赖,我们就可以开始相关的Aspet的编程了

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
      ​
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      

        

    • 创建一个pojo实体类,用来保存日志记录相关信息

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class OperationLog {
      ​
          private Integer id;
      ​
          //返回值
          private String returnValue;
      ​
          //返回值类型
          private String returnClass;
      ​
          //操作人
          private String operateUser;
      ​
          //操作时间
          private String operateTime;
      ​
          //参数值 , 键值对形式
      private String paramAndValue;
      ​
          //操作的类
          private String operateClass;
      ​
          //操作的方法
          private String operateMethod;
      ​
          //操作耗时
          private Long costTime;
      ​
      }
    • 编写自定义注解@OperateLog,在controller类中使用该注解的所有方法都会被记录到数据库中

      @Target(ElementType.METHOD)
      @Documented
      @Retention(RetentionPolicy.RUNTIME)
      public @interface OperateLog {
      }
    • 创建一个OperateAdvice的处理类

      @Component
      @Aspect
      @Slf4j
      public class OperateAdvice {
      ​
          @Around("execution(* com.sean.springaop.controller.*.*(..)) && @annotation(operateLog)" )
          public Object insertLogAround(ProceedingJoinPoint pjp , OperateLog operateLog) throws Throwable {
              log.info("*********************************** 记录日志 [start]  ****************************** ");
      ​
              OperationLog op = new OperationLog();
              //todo:从session中获取登陆用户信息
              op.setOperateUser(RandomUtil.randomNumbers(8));
      ​
              //获取输入参数
              String args = pjp.getArgs().toString();
      ​
              //获取类名
              String className = pjp.getTarget().getClass().getName();
      ​
              //获取方法名
              String methodName = pjp.getSignature().getName();
      ​
              op.setOperateClass(className);
              op.setOperateMethod(methodName);
              op.setParamAndValue(args);
      ​
              Class returnType =  pjp.getSignature().getDeclaringType();
              String declaredTypeName =  pjp.getSignature().getDeclaringTypeName();
      ​
              long startTime = System.currentTimeMillis();
              Object object = pjp.proceed();//放行
              long endTime = System.currentTimeMillis();
      ​
              op.setCostTime(endTime - startTime);
      ​
              if(object != null)
              {
                  //获取返回值类型
                  op.setReturnClass(object.getClass().getName());
                  //获取返回值
                  op.setReturnValue(object.toString());
      ​
              }else
              {
                  op.setReturnValue("void");
                  op.setReturnClass("java.lang.object");
              }
              //todo:将该对象insert到数据库中,这里使用log打印该对象数据
              log.info(op.toString());
              
              log.info(" *********************************** 记录日志 [end]  ****************************** ");
      ​
      ​
              return null;
          }
      ​
      }

      这个类,我们使用了注解 @Component 表明它将作为一个Spring Bean 被装配,使用注解 @Aspect 表示它是一个切面。

      image-20210610194124947

       

      针对这个切面类,来展开说明@Aspect切面类的编程。

      • Spring AOP 支持的切点指示器

        image-20210610195144668

      当我们查看上面展示的这些spring支持的指示器时,注意只有execution指示器是唯一的执行匹配,而其他的指示器都是用于限制匹配的。这说明execution指示器是我们在编写切点定义时最主要使用的指示器,在此基础上,我们使用其他指示器来限制所匹配的切点。

      • 5种通知类型

    注解通知
    @Before 通知方法会在目标方法调用之前执行
    @After 通知方法会在目标方法返回或异常后调用
    @AfterRetunibg 通知方法会在目标方法返回之后执行
    @AfterThrowing 通知方法会在目标方法抛出异常后执行
    @Around 通知方法会将目标方法封装起来
    • execution表达式

      execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
                  throws-pattern?)
      ​
      execution(方法修饰符(可选)  返回类型  类路径 方法名  参数  异常模式(可选)) 

    除了返回类型,方法名还有参数之外,其他都是可选的

    ret-type-pattern:可以为*表示任何返回值,全路径的类名等.

    name-pattern:指定方法名,代表所以,set,代表以set开头的所有方法. parameters pattern:指定方法参数(声明的类型), ()匹配没有参数; (..)代表任意多个参数; ()代表一个参数,但可以是任意型; (,String)代表第一个参数为任何值,第二个为String类型。

    下面给几个例子:

    1)execution(public * *(..))——表示匹配所有public方法
    2)execution(* set*(..))——表示所有以“set”开头的方法
    3)execution(* com.xyz.service.AccountService.*(..))——表示匹配所有AccountService接口的方法
    4)execution(* com.xyz.service.*.*(..))——表示匹配service包下所有的方法
    5)execution(* com.xyz.service..*.*(..))——表示匹配service包和它的子包下的方法

     

  • 相关阅读:
    LINQ查询表达式(5)
    LINQ查询表达式(4)
    并不对劲的loj2279
    并不对劲的bzoj1563:p1912:[NOI2009]诗人小G
    并不对劲的loj507
    并不对劲的loj2332
    并不对劲的loj6191
    并不对劲的bzoj4552:p2824:[HEOI2016/TJOI2016]排序
    并不对劲的bzoj3924:loj2135:p3345:[ZJOI2015]幻想乡战略游戏
    并不对劲的bzoj3295:[CQOI2011]动态逆序对
  • 原文地址:https://www.cnblogs.com/seanRay/p/14872638.html
Copyright © 2011-2022 走看看