Springboot集成AOP实现日志打印
1.创建一个Maven项目
1.1 在Maven项目的pom.xml导入springboot依赖
1.2 在Maven项目的pom.xml导入springboot-aop依赖
1.3 在Maven项目的pom.xml导入lombok依赖
<!-- 将maven项目改为springboot项目-->
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.0.2.RELEASE</version>
</parent>
<dependencies>
<!-- 导入springboot依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 导入springboot-aop依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.6.5</version>
</dependency>
<!-- 导入lombok(打印日志到控制台)依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
1.4 在项目的java目录下创建com.kai文件,并写出springboot启动类Application
package com.kai;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
1.5 在com.kai目录下创建controller文件夹与pojo文件。在controller目录中创建Login类,在pojo目录中创建User类
package com.kai.controller;
import com.kai.pojo.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Login {
@PostMapping("/login")
public String login(@RequestBody User user) {
return user.toString();
}
}
package com.kai.pojo;
import lombok.Data;
@Data
public class User {
private String name;
private String sex;
private int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
启动项目 在Postman上进行接口测试
2.完成注解与aop的实现
2.1 首先项目可以跑起来,完成接口测试。
2.2 在com.kai目录下创建aop文件夹,在aop文件夹中创建log文件夹。即我们在log文件夹中实现日志注解类与日志切面。
2.3 完成MyLogAnnotation代码的编写
package com.kai.aop.log;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;
@Retention(RetentionPolicy.RUNTIME)
// 注解放到方法上
@Target(ElementType.METHOD)
public @interface MyLogAnnotation {
// 该注解标识在方法上的作用 比如放在登录接口上 @MyLogAnnotation(name = "登录")
// 如果name没有默认值 则在使用注解时一定要赋值
String name();
String index() default "";
}
2.4 完成MyLogAspect代码的编写
package com.kai.aop.log;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
@Component
@Aspect
@Slf4j
public class MyLogAspect {
// 切入点 对标有MyLogs注解的方法进行切入
@Pointcut("@annotation(com.kai.aop.log.MyLogAnnotation)")
public void log() {
}
// 环绕通知 方法前后都可以进行加强 并返回方法结果
@Around("log()")
public Object printLog(ProceedingJoinPoint proceedingJoinPoint) {
Object proceed = null;
try {
// 打印方法执行之前的日志信息
handleBefore(proceedingJoinPoint);
// 方法执行的结果
proceed = proceedingJoinPoint.proceed();
// 打印方法执行过后的结果
handleAfter(proceed);
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
//System.lineSeparator():系统换行符
log.info("=========END=========" + System.lineSeparator());
}
return proceed;
}
private void handleBefore(ProceedingJoinPoint joinPoint) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
//获取被增强方法上的注解对象
MyLogAnnotation myLogs = getMyLogs(joinPoint);
// 在控制台打印 方法接口 信息
log.info("=========Start=========");
// 方法接口的路径
log.info("URL :{}", request.getRequestURL());
// 方法注解上的标识内容
log.info("Name :{}", myLogs.name());
// 方法的名称
log.info("HTTP Method :{}", request.getMethod());
// 方法的包路径
log.info("Class Method :{}.{}", joinPoint.getSignature().getDeclaringTypeName(), ((MethodSignature) joinPoint.getSignature()).getName());
// IP地址
log.info("IP :{}", request.getRemoteHost());
// 方法接口中的参数
log.info("Request Args :{}", Arrays.toString(joinPoint.getArgs()));
}
// 打印方法结果
private void handleAfter(Object proceed) {
log.info("Response :{}", proceed);
}
// 获取注解内容
private MyLogAnnotation getMyLogs(ProceedingJoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
MyLogAnnotation myLogs = methodSignature.getMethod().getAnnotation(MyLogAnnotation.class);
return myLogs;
}
}
2.5 在controller目录下的Login接口的login方法上加上@MyLogAnnotation
package com.kai.controller;
import com.kai.aop.log.MyLogAnnotation;
import com.kai.pojo.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class Login {
@PostMapping("/login")
@MyLogAnnotation(name = "登录")
public String login(@RequestBody User user) {
return user.toString();
}
}
2.6 重启项目 测试login接口 控制台打印
2022-05-26 17:44:52.508 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : =========Start=========
2022-05-26 17:44:52.508 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : URL :http://localhost:8080/login
2022-05-26 17:44:52.509 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : Name :登录
2022-05-26 17:44:52.509 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : HTTP Method :POST
2022-05-26 17:44:52.509 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : Class Method :com.kai.controller.Login.login
2022-05-26 17:44:52.509 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : IP :0:0:0:0:0:0:0:1
2022-05-26 17:44:52.509 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : Request Args :[User{name='曹操', sex='男', age=22}]
2022-05-26 17:44:52.511 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : Response :User{name='曹操', sex='男', age=22}
2022-05-26 17:44:52.511 INFO 17912 --- [nio-8080-exec-1] com.kai.aop.log.MyLogAspect : =========END=========
则成功
2.7 也可以使用前置通知与后置通知
package com.kai.aop.log;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
@Component
@Aspect
@Slf4j
public class MyLogAspect {
// 切入点 对标有MyLogs注解的方法进行切入
@Pointcut("@annotation(com.kai.aop.log.MyLogAnnotation)")
public void log() {
}
//请求method前打印内容
@Before("log()")
public void methodBefore(JoinPoint joinPoint) {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
//打印请求内容
log.info("===============请求内容===============");
log.info("请求地址:" + request.getRequestURL().toString());
log.info("请求方式:" + request.getMethod());
log.info("请求类方法:" + joinPoint.getSignature());
log.info("请求类方法参数:" + Arrays.toString(joinPoint.getArgs()));
log.info("===============请求内容===============");
}
//在方法执行完结后打印返回内容
@AfterReturning(returning = "o", pointcut = "log()")
public void methodAfterReturing(Object o) {
log.info("--------------返回内容----------------");
log.info("Response内容:" + o);
log.info("--------------返回内容----------------");
}
}