1、springboot中使用AOP切面完成全局日志

1、springboot中使用AOP切面完成全局日志

可选择在控制台输出日志或者收集日志信息存储进数据库中

1、在配置 AOP 切面之前,我们需要了解下 aspectj 相关注解的作用:

  • @Aspect:作用是把当前类标识为一个切面、供容器读取
  • @Pointcut:(哪些方法或者类需要进行AOP织入)定义一个切点,后面跟随一个表达式,表达式可以定义为某个 package 下的方法,也可以是自定义注解等;
  • 切点定义好后,就是围绕这个切点做文章了:
    • @Before: 在切点之前,织入相关代码;
    • @After: 在切点之后,织入相关代码;
    • @AfterReturning: 在切点返回内容后,织入相关代码,一般用于对返回值做些加工处理的场景;
    • @AfterThrowing: 用来处理当织入的代码抛出异常后的逻辑处理;
    • @Around: 在切入点前后织入代码,并且可以自由的控制何时执行切点;

2、导入依赖

注:第二个依赖可选择不用,本质就是将数据 JSON 化、使用阿里的也行(后期出错再改)

<!-- aop 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <!-- 用于日志切面中,以 json 格式打印出入参(本来使用阿里的 FASTJSON, 但是对于文件上传的接口,打印参数会报错,换为 Gson) -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

3、配置文件中声明

注:可不用声明,默认开启且使用的是 JDK(即接口方式的动态代理,如果需要使用类的动态代理就需要将proxy-target-class: true设置为true,默认是false)

这里其实不需要进行下面的配置,只是提一下

#spring配置
spring:
  #切面启用
  aop:
    proxy-target-class: true
    auto: true

4、编写全局日志处理

定义一个类,类名为:WebLogAspect(可任意取)

注:下面只是实现了在控制台打印日志信息,我们在末尾可添加一个插入方法,插入此次收获的所有信息存储进日志表中

同时,最好只插入增删改三者,因其涉及对数据的变动,查询不会变动数据可忽略(不然日志信息太多)

扫描二维码关注公众号,回复: 15279002 查看本文章

其次,此实现只AOP类控制层的方法,其他层也可照此。

package com.zou.config;


import com.google.gson.Gson;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class WebLogAspect {
    
    

    // 获取日志类,方便直接在控制台输出统一格式的日志信息
    private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
	
    /*
    方式1:controller包下所有类中的开头带有insert的方法
    	@Pointcut("execution(public * com.zou.controller.*.insert*(..))")
    方式2:
		@Pointcut("execution(public * com.ldb.admin.controller.*.create*(..))||" +
            "execution(public * com.ldb.admin.controller.*.update*(..))||" +
            "execution(public * com.ldb.admin.controller.*.delete*(..))")
    	注:可使用 || &&
     * 1)execution(public * (..))——表示匹配所有public方法
     * 2)execution( set(..))——表示所有以“set”开头的方法
     * 3)execution( com.xyz.service.AccountService.(..))——表示匹配所有AccountService接口的方法
     * 4)execution( com.xyz.service..(..))——表示匹配service包下所有的方法
     * 5)execution(* com.xyz.service...(..))——表示匹配service包和它的子包下的方法
     */
    
    /** 以 controller 包下定义的所有请求为切入点 */
    @Pointcut("execution(public * com.zou.controller.*.*(..))")
    public void webLog() {
    
    }

    /**
     * 在切点之前织入
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()") // webLog():是你@Pointcut注解的方法名
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    
    
        // 开始打印请求日志
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        // 打印请求相关参数
        logger.info("========================================== Start ==========================================");
        // 打印请求 url
        logger.info("URL            : {}", request.getRequestURL().toString());
        // 打印 Http method
        logger.info("HTTP Method    : {}", request.getMethod());
        // 打印调用 controller 的全路径以及执行方法
        logger.info("Class Method   : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
        // 打印请求的 IP
        logger.info("IP             : {}", request.getRemoteAddr());
        // 打印请求入参
        logger.info("Request Args   : {}", new Gson().toJson(joinPoint.getArgs()));

        // 获取 @ApiOperation("") 注解中的信息,此注解属于swagger的,但也能获取,其他注解可举一反三
        ApiOperation apiOperation = null;
        
        // JoinPoint:oinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.大概就是获取目标方法的一些信息
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        apiOperation = ms.getMethod().getDeclaredAnnotation(ApiOperation.class);
        if (apiOperation != null) {
    
     ;
            logger.info("操作   : {}", apiOperation.value());
        }
    }

    /**
     * 在切点之后织入
     * @throws Throwable
     */
    @After("webLog()")
    public void doAfter() throws Throwable {
    
    
        logger.info("=========================================== End ===========================================");
        // 每个请求之间空一行
        logger.info("");
    }

    /**
     * 环绕
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around("webLog()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    
    
        long startTime = System.currentTimeMillis();
        Object result = proceedingJoinPoint.proceed();
        // 打印出参
        logger.info("Response Args  : {}", new Gson().toJson(result));
        // 执行耗时
        logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
        return result;
    }


}


JoinPoint

常用API
方法名	功能
Signature getSignature();	获取封装了署名信息的对象,在该对象中可以获取到目标方法名,所属类的Class等信息
Object[] getArgs();	获取传入目标方法的参数对象
Object getTarget();	获取被代理的对象
Object getThis();	获取代理对象

ProceedingJoinPoint对象

ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中,
添加了以下两个方法。

Object proceed() throws Throwable //执行目标方法 
Object proceed(Object[] var1) throws Throwable //传入的新的参数去执行目标方法 

猜你喜欢

转载自blog.csdn.net/qq_43483251/article/details/128870308
今日推荐