SpringBoot使用自定义日志注解,配置切面

发布时间 2023-06-14 19:48:51作者: alexmelt

1. 使用技巧

以下是需要注意的部分:

  1. 在环绕通知中使用ProceedingJoinPoint,控制目标方法的运行。
  2. 在其他通知类型中使用JoinPoint
  3. 如果使用JoinPoint 则必须位于参数的第一位。
  4. ProceedingJoinPoint 中有特殊的方法proceed()。
  5. 当有多个切面时,使用@Order(11)来指定注解的优先级。
    1. 在切点之前,@Order 从小到大被执行,也就是说越小的优先级越高;
    2. 在切点之后,@Order 从大到小被执行,也就是说越大的优先级越高;
  6. 切面流程:环绕前置--> 普通前置--> 目标方法执行--> 普通返回 --> 普通后置--> 环绕返回 -->环绕后置

2. 使用流程

  1. 在pom中引入相关依赖。

    <properties>
        <fastjson.version>1.2.83</fastjson.version>
    </properties>
    <!--aop依赖-->
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <!--用于日志切面,以json打印出入参数-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>${fastjson.version}</version>
    </dependency>
    
  2. 在annotation包下创建WebLog注解。

    package com.zhao.common.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * 配置系统日志
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface WebLog {
        /**
         * 日志描述信息
         */
        String description() default "";
    }
    
  3. 在aspect包下创建WebLogAspect类。

    package com.zhao.common.aspect;
    
    import com.google.gson.Gson;
    import com.zhao.common.annotation.WebLog;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.annotation.Profile;
    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.lang.reflect.Method;
    
    /**
     * 系统日志,切面处理类
     */
    @Aspect // 声明该类是一个注解类
    @Component // 注册为组件
    @Profile({"dev", "test"}) // 使用场景:在配置文件中配置
    public class WebLogAspect {
        /**
         * 使用slf4j注解
         */
        private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
        /**
         * 换行符
         */
        private static final String LINE_SEPARATOR = System.lineSeparator();
    
        /**
         * 以自定义的日志注解作为切入点
         */
        @Pointcut("@annotation(com.zhao.annotation.WebLog)")
        public void logPointCut() {
    
        }
    
        /**
         * 在切入点前
         */
        @Before("logPointCut()")
        public void doBefore(JoinPoint joinPoint) throws Throwable {
            // 开始打印日志请求
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            // 获取@WebLog注解的描述信息
            String methodDescription = getAspectLogDescription(joinPoint);
            // 打印请求 url
            logger.info("URL            : {}", request.getRequestURL().toString());
            // 打印描述信息
            logger.info("Description    : {}", methodDescription);
            // 打印 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   : {}", JSON.toJSON(joinPoint.getArgs()));
        }
    
        /**
         * 在切点之后织入
         */
        //@After("logPointCut()")
        //public void doAfter() throws Throwable {
        //    // 接口结束后换行,方便分割查看
        //}
    
        /**
         * 环绕
         */
        @Around("logPointCut()")
        public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            logger.info("========================================== Start ==========================================");
            long startTime = System.currentTimeMillis();
            Object result = proceedingJoinPoint.proceed();
            // 打印出参
            logger.info("Response Args  : {}", JSON.toJSON(result));
            // 执行耗时
            logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
            // 接口结束后换行,方便分割查看
            logger.info("=========================================== End ===========================================" + LINE_SEPARATOR);
            return result;
        }
    
    
        /**
         * 获取切面注解的详细描述
         */
        public String getAspectLogDescription(JoinPoint joinPoint) throws Throwable {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            StringBuilder description = new StringBuilder();
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == arguments.length) {
                        description.append(method.getAnnotation(WebLog.class).description());
                        break;
                    }
                }
            }
            return description.toString();
        }
    
    }
    
  4. 在application配置文件中设置环境

    Spring:
      profiles:
        active: dev
    
  5. 使用时在方法上添加注解即可。