通过AOP 实现打印全局日志

举报
ksh1998 发表于 2021/12/25 23:52:16 2021/12/25
【摘要】 常用注解 1、@Before 修饰一个方法时,该方法将作为Before增强处理 使用@Before修饰事,需要指定一个value属性值,该属性值指定一个切入点表达式(既可以是一个已有的切入点,也可以直...

常用注解

1、@Before

修饰一个方法时,该方法将作为Before增强处理
使用@Before修饰事,需要指定一个value属性值,该属性值指定一个切入点表达式(既可以是一个已有的切入点,也可以直接定义切入点表达式),用于指定该增强处理将被织入哪些切入点
表示在切入点执行前需要进行的操作或者需要执行的方法

2、@After

同Before
表示在切入点执行后,进行哪些操作
通常用于资源释放

3、 @Around

Around增强处理是功能比较强大的增强处理;
近似等于Before增强处理和AfterReturning增强处理的总和
既可以在执行目标方法之前织入增强动作,也可以在执行目标方法之后织入增强动作
Around增强处理可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值
Around增强处理虽然功能强大,但通常需要在线程安全的环境下使用,所以一般用Before和AfterReturning增强处理能解决的问题,不建议用Around
如果需要目标方法执行之前和之后共享某种状态数据,则应该考虑使用Around增强处理;尤其是需要改变目标方法的返回值时,则只能使用Around增强处理了
@Around增强处理事,需要指定一个value属性,该属性指定该增强处理被植入的切入点
当定义一个Around增强处理方法时,该方法的第一个形参必须是ProceedingJoinPoint类型(至少包含一个形参),在增强处理方法体内,调用ProceedingJoinPoint参数的proceed()方法才会执行目标方法——这就是Around增强处理可以完全控制目标方法的执行时机、如何执行的关键;如果程序没有调用ProceedingJoinPoint参数的proceed()方法,则目标方法不会被执行。
调用ProceedingJoinPoint参数的proceed()方法时,还可以传入一个Object[]对象作为参数,该数组中的值将被传入目标方法作为执行方法的实参

代码实现

maven依赖

   //打印日志的依赖,lombok里还包含了实体注解data
    <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>test</scope>
        </dependency>
        //aop依赖
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
        //引入lombok的日志依赖
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

aop
学习了Springboot的都知道,SpringBoot默认配置了AOP。这也是SpringBoot的好处,很多默认配置都给我们简化了。

 package com.example.springbootorderrabbitmqproducer.AOP;



import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * @author 康世行
 * @Title:
 * @Package com.example.springbootorderrabbitmqproducer.AOP
 * @Description: 全局打印日志
 * @date 2021-12-09 8:05
 */
@Aspect
@Component
@Slf4j
public class Logs {
    /** 以 controller 包下定义的所有请求为切入点 */
    @Pointcut("execution(public * com.example.springbootorderrabbitmqproducer.controller..*.*(..))")
    public void webLog() {}
    /**
     * 在切点之前织入
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        try{
            // 开始打印请求日志
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 打印请求相关参数
            log.info("========================================== Start ==========================================");
            // 打印请求 url
            log.info("URL      : {}", request.getRequestURL().toString());
            // 打印 Http method
            log.info("HTTP Method  : {}", request.getMethod());
            // 打印调用 controller 的全路径以及执行方法
            log.info("Class Method  : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
            // 打印请求的 IP
            log.info("IP       : {}", request.getRemoteAddr());
            // 打印请求入参
            String args = new Gson().toJson(joinPoint.getArgs());
            log.info("Request Args  : {}", args);
        } catch (Exception e) {
            log.error("日志打印失败");
        }
    }
    /**
     * 在切点之后织入
     * @throws Throwable
     */
    @After("webLog()")
    public void doAfter() throws Throwable {
        log.info("=========================================== End ===========================================");
        // 每个请求之间空一行
        log.info("");
    }
    /**
     * 环绕
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        // 打印出参
         log.info("Response Args : {}", new Gson().toJson(result));
        // 执行耗时
        log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
        return result;
    }


}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

controller

 package com.example.springbootorderrabbitmqproducer.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 康世行
 * @Title:
 * @Package com.example.springbootorderrabbitmqproducer.controller
 * @Description: 测试日志controller
 * @date 2021-12-09 8:27
 */
@RestController
@RequestMapping("/test")
public class testController {
    @GetMapping("/info/{mesg}")
    public String testLoginfo(@PathVariable("mesg") String mesg){
       return mesg;
    }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

实现效果

方法执行之前

 : ========================================== Start ==========================================
2021-12-09 08:39:34.556  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : URL      : http://localhost:8080/test/info/ksh
2021-12-09 08:39:34.556  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : HTTP Method  : GET
2021-12-09 08:39:34.557  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : Class Method  : com.example.springbootorderrabbitmqproducer.controller.testController.testLoginfo
2021-12-09 08:39:34.558  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : IP       : 0:0:0:0:0:0:0:1
2021-12-09 08:39:34.560  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : Request Args  : ["ksh"]


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

方法执行之后

 2021-12-09 08:39:34.564  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : =========================================== End ===========================================
2021-12-09 08:39:34.564  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : 


  
 
  • 1
  • 2
  • 3

方法执行前后

 2021-12-09 08:39:34.564  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : Response Args : "ksh"
2021-12-09 08:39:34.564  INFO 32148 --- [nio-8080-exec-1] c.e.s.AOP.Logs                           : Time-Consuming : 9 ms


  
 
  • 1
  • 2
  • 3

思考

如下代码

思考一,

Object result = proceedingJoinPoint.proceed();方法出现的位置会影响Around的执行吗?

思考二,

Object result = proceedingJoinPoint.proceed(); 方法出现在
long startTime = System.currentTimeMillis();
log.info(“进入指定路径”+proceedingJoinPoint.getTarget()+“controller”);这两行代码下面,方法执行前会输出什么? 先执行Befour还是先执行Around?

  /**
     * 环绕
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        log.info("进入指定路径"+proceedingJoinPoint.getTarget()+"controller");
        Object result = proceedingJoinPoint.proceed();
        // 打印出参
         log.info("Response Args : {}", new Gson().toJson(result));
        // 执行耗时
        log.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
        log.info("方法执行完毕");
        return result;
    }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

揭晓答案

思考一,
会影响Around的方法执行,Object result = proceedingJoinPoint.proceed() 如果出现在第一行,那么日志就不会打印进入方法之前的日志。(指的是Around方法不会打印进入方法之前的日志,并不会影响Before方法)
原因:
proceedingJoinPoint.proceed() ,proceed() 这个方法是继续执行目标方法,就是被切点切入的方法。可以使用这个方法控制被切方法的运行时机。

思考二,
这个和上面的around代码相反,会在进入方法之前打印 进入了那个controller的全路径,从结果上看around的优先级比Before的优先级高(具体的待研究)
输出结果:
在这里插入图片描述

欢迎一键三连,支持下小编!!!

文章来源: kangshihang.blog.csdn.net,作者:康世行,版权归原作者所有,如需转载,请联系作者。

原文链接:kangshihang.blog.csdn.net/article/details/121805395

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。