SpringBoot -- 自定义注解 & AOP打印日志

前言:在开发过程中,开发人员难免会遇到打印参数输出的问题,这时候你还在一个一个打印吗?何不尝试封装一下,利用AOP通过管理日志打印。今天我们就通过自定义注解的方式,在SpringBoot中来实现AOP切面统一打印出入参数。有需要的小伙伴可以收藏一波~

一、先看看AOP切面日志输出效果:

SpringBoot -- 自定义注解 & AOP打印日志_第1张图片

 从上图可以看到,对于每个请求,实现了对出入参的打印:

  • URL: 请求接口地址;
  • Description: 接口描述信息;
  • HTTP Method: 请求的方法,例如POST、GET、PUT、DELETE等;
  • Class Method: 被请求的方法路径,包名 + 方法名;
  • IP: 请求方的IP地址;
  • Request Args: 请求入参,以JSON格式输出;
  • Response Args: 响应出参,以JSON格式输出;
  • Time-Consuming: 请求耗时,以此估算每个接口的性能指数;

二、添加AOP Maven依赖

在web项目的pom.xml文件中添加以下依赖:



	org.springframework.boot
	spring-boot-starter-aop

		


	com.google.code.gson
	gson
	2.8.5

三、自定义日志注解

package com.layduo.common.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义日志注解
 * 
 * @author layduo
 * @createTime 2019年11月5日 下午6:33:29
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface WebLog {

	/**
	 * 日志描述信息
	 * 
	 * @return
	 */
	String description() default "";
}
  • :什么时候使用该注解,我们定义为运行时;

  • :注解用于什么地方,我们定义为作用于方法上;

  • :注解是否将包含在 JavaDoc 中;

  • :注解名为 WebLog;

  • :定义一个属性,默认为空字符串;

四、配置 AOP 切面

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

  • @Aspect:声明该类为一个注解类;

  • @Pointcut:定义一个切点,后面跟随一个表达式,表达式可以定义为切某个注解,也可以切某个 package 下的方法;

切点定义好后,就是围绕这个切点做文章了:

  • @Before: 在切点之前,织入相关代码;

  • @After: 在切点之后,织入相关代码;

  • @AfterReturning: 在切点返回内容后,织入相关代码,一般用于对返回值做些加工处理的场景;

  • @AfterThrowing: 用来处理当织入的代码抛出异常后的逻辑处理;

  • @Around: 环绕,可以在切入点前后织入代码,并且可以自由的控制何时执行切点;

  • 执行顺序:@Around --> @Before --> 接口中逻辑代码 --> @After --> @AfterReturning

package com.layduo.framework.aspectj;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
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 com.google.gson.Gson;
import com.layduo.common.annotation.WebLog;

/**
* @author layduo
* @createTime 2019年11月5日 下午6:36:57
*/
@Aspect
@Component
public class WebLogAspect {

	 private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);

     private static final String LINE_SEPARATOR = System.lineSeparator();
     
     /**
      * 以自定义注解为切点
      */
     @Pointcut("@annotation(com.layduo.common.annotation.WebLog)")
     public void webLog(){}

     /**
      * 环绕
      * @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;
     }

     /**
      * 在切点前织入
      * @param joinPoint
      */
     @Before("webLog()")
     public void doBefore(JoinPoint joinPoint) throws Exception {
         //开始打印请求日志
         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
         HttpServletRequest request = attributes.getRequest();

         //获取注解的描述信息
         String methodDescription = getAspectLogDescription(joinPoint);
         // 打印请求相关参数
         logger.info("========================================== Start ==========================================");
         // 打印请求 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   : {}", new Gson().toJson(joinPoint.getArgs()));
     }

     /**
      * 在切点后织入
      */
     @After("webLog()")
     public void deAfter(){
         logger.info("==========================================  End  ==========================================" + LINE_SEPARATOR);
     }

     /**
      * 获取切面注解的描述
      * @param joinPoint 切点
      * @return 描述信息
      * @throws Exception
      */
     public String getAspectLogDescription(JoinPoint joinPoint)
             throws Exception {
         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();
     }
}

五、如何使用自定义注解,进行日志输出

在controller层,针对某个想输出日志的方法,在其方法上添加自定义日志输出注解@WebLog就行,不需要打印可以不添加:

@RequestMapping("/testWebLog/{name}")
@ResponseBody
@WebLog(description = "测试自定义注解@WebLog")
public String testWebLog(@PathVariable("name") String name) {
	return "Welcome to " + name + "!";
}

本文借鉴于:如何优雅地在 Spring Boot 中使用自定义注解,AOP 切面统一打印出入参日志

项目源码已上传github: https://github.com/builthuLin/layduo.git  有需要的自己fork一下~

你可能感兴趣的:(Spring,Boot)