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上进行接口测试

spring boot 启动后打印 数据库配置 springboot打印机打印_postman

2.完成注解与aop的实现

2.1 首先项目可以跑起来,完成接口测试。

2.2 在com.kai目录下创建aop文件夹,在aop文件夹中创建log文件夹。即我们在log文件夹中实现日志注解类与日志切面。

spring boot 启动后打印 数据库配置 springboot打印机打印_postman_02


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("--------------返回内容----------------");
    }

}
注解与aop结合实现简单日志打印已完成