SpringBoot+AOP实现日志功能(自定义注解的使用)

本文最后更新于2022.09.22-07:36,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥

什么是AOP?

AOP(Aspect-Oriented Programming:面向切面编程):是指将那些与业务无关,却被多个业务模块所共同调用逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,提升系统的可维护性。

AOP的作用

AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

AOP的应用场景

比如典型的AOP的应用场景:

  • 日志记录

  • 事务管理

  • 权限验证

  • 性能监测

AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离。

步骤

详细配置请看

10. Spring-AOP的实现
本文最后更新于2022.09.22-07:36,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥。AOP的概念 Aspect Oreinted Programming 面向切面编程,通……
11.Spring-AOP的原理
本文最后更新于2022.09.22-07:36,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥。AOP Aspect Oriented Programming:面向切面编程 OOP Obje……

1,导入maven配置

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.66</version>
        </dependency>

2,配置一个自定义注解

package com.example.aop.config;

import java.lang.annotation.*;

/**
 * @Program: aop
 * @ClassName SysAnnotation
 * @Description: 自定义系统操作注解
 * @Author: liutao
 * @DateTime: 2022/6/5 2:32
 * @Version: 1.0
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysAnnotation {
    String operMoudle() default "";//操作模块
    String operMethod() default "";//操作方法
    String operDes() default "";//操作描述
}

 3,创建一个测试接口 TestController

package com.example.aop.controller;

import com.example.aop.config.SysAnnotation;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Program: aop
 * @ClassName TestController
 * @Description: aop实现日志测试接口
 * @Author: liutao
 * @DateTime: 2022/6/5 2:29
 * @Version: 1.0
 */
@RestController
public class TestController {
    @RequestMapping("test")
    @SysAnnotation(operMoudle = "测试接口",operMethod="test", operDes = "测试")
    public String test(String param){
        return param;
    }

}

4,定义切面

package com.example.aop.log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.aop.config.SysAnnotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
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.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

/**
 * @Program: aop
 * @ClassName SysLogger
 * @Description: aop日志具体实现
 * @Author: liutao
 * @DateTime: 2022/6/5 2:38
 * @Version: 1.0
 */
@Aspect
@Component
public class SysLogger {
    private final Logger logger = LoggerFactory.getLogger(SysLogger.class);
    @Pointcut("@annotation(com.example.aop.config.SysAnnotation)")
    public void operLoggerPointCut(){
        logger.info("===============系统操作日志===============");
    }

    @Pointcut("execution(public * com.example.aop.controller.*.*(..))")
    public void controllerMethod(){}

    @Before("operLoggerPointCut()")
    public void logBefore(JoinPoint joinPoint){
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes)requestAttributes).getRequest();
        StringBuilder requestLog = new StringBuilder();
        Signature signature = joinPoint.getSignature();
        Method method = ((MethodSignature) signature).getMethod();
        SysAnnotation sysAnnotation = method.getAnnotation(SysAnnotation.class);//利用反射获取到自定义注解
        // 打印请求内容
        logger.info("===============请求内容开始===============");
        logger.info("操作模块:"+sysAnnotation.operMoudle());
        logger.info("操作方法:"+sysAnnotation.operMethod());
        logger.info("操作描述:"+sysAnnotation.operDes());
        logger.info("请求地址:" + request.getRequestURL().toString());
        logger.info("请求IP:" + request.getRemoteAddr());
        logger.info("请求方式:" + request.getMethod());
        logger.info("请求类方法:" + joinPoint.getSignature());
        logger.info("请求类方法参数值:" + Arrays.toString(joinPoint.getArgs()));

        // 处理请求参数
        String[] paramNames = ((MethodSignature) signature).getParameterNames();
        Object[] paramValues = joinPoint.getArgs();
        int paramLength = null == paramNames ? 0 : paramNames.length;
        if (paramLength == 0) {
            requestLog.append("请求参数 = {} ");
        } else {
            requestLog.append("请求参数 = [");
            for (int i = 0; i < paramLength - 1; i++) {
                requestLog.append(paramNames[i]).append("=").append(JSONObject.toJSONString(paramValues[i])).append(",");
            }
            requestLog.append(paramNames[paramLength - 1]).append("=").append(JSONObject.toJSONString(paramValues[paramLength - 1])).append("]");
        }
        logger.info("请求参数明细:"+requestLog.toString());
        logger.info("===============请求内容结束===============");
    }

    @AfterReturning(returning = "o", pointcut = "operLoggerPointCut()")
    public void logResultVOInfo(Object o){
        logger.info("--------------返回内容开始----------------");
        logger.info("Response内容:" + JSON.toJSONString(o));
        logger.info("--------------返回内容结束----------------");
    }

    @AfterThrowing(pointcut = "controllerMethod()", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        List<Object> args = Arrays.asList(joinPoint.getArgs());
        System.out.println("连接点方法为:" + methodName + ",参数为:" + args + ",异常为:" + ex);

    }
}

 效果图

使用主流日志框架

市场上存在非常多的日志框架。 JUL(java.util.logging),JCL(ApacheCommons Logging),Log4j,Log4j2,Logback、 SLF4j、 jboss-logging等。
Spring Boot在框架内容部使用JCL,spring-boot-starter-logging采用了slf4j+logback的形式,Spring Boot也能自动适配(jul、 log4j2、 logback) 并简化配置。

阅读剩余
THE END