通过AOP 实现打印全局日志
常用注解
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
- 点赞
- 收藏
- 关注作者
评论(0)