运用Spring Aop,一个注解实现日志记录 1. 介绍
我们都知道Spring框架的两大特性分别是 IOC (控制反转)和 AOP (面向切面),这个是每一个Spring学习视频里面一开始都会提到的。在日常项目中,我们也会经常使用IOC控制反转,但是却感觉AOP很少会运用到。其实AOP大有用处,甚至可以让你偷偷懒。
举一个例子,假如现在要让你记录每一个请求的请求IP,请求的方法,请求路径,请求的参数,返回参数,你会怎么做?你会想,那简单啊,我直接 log.info("xxxx") 输出日志不行吗,简单!可是你要想清楚,每个请求请求的方法不一定是同一个,有一些请求可能请求编辑方法,另外一些请求可能请求登录方法,这么多方法,你每一个方法下面都重复写了差不多6,7行重复代码,你觉得这好吗?
这里,如果我们使用Aop来记录日志,那是再好不过了。我们可以看看一个方法的执行过程来理解AOP。
下面再来看使用AOP后的执行过程。
AOP是面向切面编程,面向切面思想就是让我们把程序想象成一条一条管道连接起来的大管道,而AOP就是在管道和管道之间的过滤网,能够在不影响管道的情况下对管道中传输的数据进行记录,修改。
使用AOP我们可以很方便地进行操作日志记录,性能日志记录,请求日志记录,事务操作,安全管理等。这么说可能很抽象,再详细点说就是各种日志记录我们可以利用AOP来进行记录,而不用在业务逻辑代码中插入,安全管理就是我们可以在请求进来前对请求中的数据进行解密,在请求返回的时候对数据进行加密。这么说AOP很像Java里面的拦截器,过滤器和监听器的结合。
具体AOP的原理就不细讲了,那是另外一篇文章了,有关于动态代理。
2. 实践说了这么多,大白话就是AOP能让我们在不影响原代码的基础上,对代码功能进行添加,修改
在实现日志记录功能前,我们要先复习一下 Spring Aop 里面的通知顺序(连接点,通知还不知道是什么的,先去B站看一下Spring初级教程)。
先把Aop的starter依赖添加进pom文件中。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> 2.1 定义注解那现在我们来自定义一个注解,目的是标注该注解的方法将会记录调用该方法的请求信息
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface MyLog { String value() default ""; }注解不是本篇重点,有兴趣的童鞋可以搜一下。
2.2 切面类定义我们的日志记录切面类,切面类中记录请求的信息。
@Component @Aspect @Slf4j public class LogAspect { //切入点为自定义注解 @Pointcut("@annotation(com.example.springaopdemo.demo2.MyLog)") public void MyLog(){} @Before("MyLog()") public void Before(JoinPoint jp){ //获取HttpServletRequest对象 ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); assert requestAttributes != null; HttpServletRequest request = requestAttributes.getRequest(); log.info("==========请求信息=========="); log.info("请求链接 : {}",request.getRequestURL().toString()); log.info("Http Method : {}",request.getMethod()); log.info("Class Method : {}.{}",jp.getSignature().getDeclaringTypeName(),jp.getSignature().getName()); log.info("Ip : {}",request.getRemoteAddr()); log.info("Args : {}", Arrays.asList(jp.getArgs())); } @Around("MyLog()") public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed(); log.info("执行时间 : {} ms", System.currentTimeMillis() - startTime); log.info("返回参数 : {}", result); return result; } }通过 @Around 环绕通知我们可以进行简单的性能记录,如果加上 Oshi 我们甚至可以记录执行该方法前后的CPU,内存占用率。
Oshi是Java的免费基于JNA的操作系统和硬件信息库,Github地址是:https://github.com/oshi/oshi
它的优点是不需要安装任何其他本机库,并且旨在提供一种跨平台的实现来检索系统信息,例如操作系统版本,进程,内存和CPU使用率,磁盘和分区,设备,传感器等。