SpringBoot 的切面日志配置
Spring 框架的一个关键组件是面向方面的编程(AOP)框架。面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样的常见的例子,如日志记录、审计、声明式事务、安全性和缓存等。
其底层是基于Java的动态代理的技术来实现操作的,即在不改变原方法的代码的情况下,对其方法进行方法的增强,降低方法之间的依赖,从而达到解耦的效果。
这里我们利用 Spring 的 AOP 特性来实现日志的记录
一、新建项目和测试接口
新建一个SpringBoot项目,再添加一个测试接口
1 2 3 4 5 6 7 8 9 10 11 12 13
| package me.fabian4.springbootlog;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
@RestController public class Controller {
@GetMapping() public String test(String text){ return "msg:" + text; } }
|
二、自定义注解 @Log
1 2 3 4 5 6 7 8 9 10 11 12
| package me.fabian4.springbootlog;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { String value() default ""; }
|
将注解添加到方法上面
1 2 3 4 5
| @Log("测试接口") @GetMapping() public String test(String text){ return "msg:" + text; }
|
三、配置切面
1. 导入切面解析依赖
1 2 3 4 5
| <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.5</version> </dependency>
|
2. 配置切面
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
| package me.fabian4.springbootlog;
import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component;
import java.util.Arrays;
@Slf4j @Aspect @Component public class LogAspect {
@Pointcut("@annotation(me.fabian4.springbootlog.Log)") public void logPointcut() { }
@Around("logPointcut()") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { Object result; result = joinPoint.proceed(); log.info("方法参数:{}", Arrays.toString(joinPoint.getArgs())); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); me.fabian4.springbootlog.Log aopLog = signature.getMethod().getAnnotation(me.fabian4.springbootlog.Log.class); log.info("注解内容:{}", aopLog.value()); log.info("方法名称:{}.{}()", joinPoint.getTarget().getClass().getName(), signature.getName()); log.info("方法返回:{}", result.toString()); return result; }
@AfterThrowing(pointcut = "logPointcut()", throwing = "e") public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { } }
|
四、测试结果
我们访问一下接口来测试结果

五、扩展
- 定义一个 Log 类来方便日志的数据传输和数据库存储
- 可以通过一些方法从请求头中获取来访者 ip 等信息并存储到数据中
- 可以从认证信息中获取到来访者的标识等信息