springboot中自定义注解和将请求对象及返回结果以xml格式进行日志打印

SpringBoot中自定义注解和将请求对象及返回结果以xml格式进行日志打印

一.添加以下依赖

		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.24</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

二.编写自定义注解

元注解
Target:描述了注解修饰的对象范围,取值在java.lang.annotation.ElementType定义,常用包括:
  • ElementType.METHOD:用于描述方法
  • ElementType.PACKAGE :用于描述包
  • ElementType.PARAMETER :用于描述方法变量
  • ElementType.TYPE:用于描述类、接口或enum类型
  • ElementType.LOCAL_VARIABLE:用于描述局部变量
  • ElementType.FIELD:用于描述字段
  • ElementType.CONSTRUCTOR:用于描述构造方法
Retention:表述注解保留时间的长短,取值在java.lang.annotation.RetentionPolicy中,取值:
  • RetentionPolicy.SOURCE:在源文件中有效,编译过程中会被忽略
  • RetentionPolicy.CLASS:随源文件一起编译在class文件中,运行时忽略
  • RetentionPolicy.RUNTIME:在运行时有效,可以通过发反射读取
只有定义为RetentionPolicy.RUNTIME时,我们才能通过注解反射获取注解

@Inherited 允许子类继承,可以不加

@Documented 注解应该被 javadoc工具记录,可以不加

例如:

package com.gremlin.annotation;

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

/**
 * @className: CustomLog
 * @author: gremlin
 * @version: 1.0.0
 * @description: 自定义注解的使用
 * @date: 2022/8/14 23:03
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomLog {
    
    

    /**
     * 接口解释,用于做什么
     */
    String notes() default "";

    /**
     * 接口请求地址
     */
    String reqUrl() default "";
}

然后再在要调用注解的方法上写@CustomLog(notes = “获取用户信息,测试”),
通过切面来展示数据

三.将请求对象,和返回数据转换为xml格式输出为日志

编写日志切面类
package com.gremlin.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gremlin.annotation.CustomLog;
import com.gremlin.utils.JsonFormatTool;
import com.gremlin.utils.XmlUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
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.Objects;
import java.util.UUID;

/**
 * @className: LogAspect
 * @author: gremlin
 * @version: 1.0.0
 * @description:
 * @date: 2022/8/14 23:07
 */
@Slf4j
@Aspect
@Component
public class LogAspect {
    
    

    @Autowired
    private ObjectMapper objectMapper;

    @Around(value = "@annotation(customLog)")
    public Object around(ProceedingJoinPoint point, CustomLog customLog) throws Throwable {
    
    
        HttpServletRequest request =
                ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

        // 开始时间
        long startTime = System.currentTimeMillis();
        UUID uuid = UUID.randomUUID();
        // 获取CustomLog注解参数值
        String notes = customLog.notes();
        // 获取请求地址
        String reqPath = request.getServletPath();
        // 方法参数
        Object[] args = point.getArgs();
        Object o = args[0];
        String str = XmlUtil.convertToXml(o);
        log.info("CustomLog : {},请求接口地址 : {},接口用途 : {},请求数据...{}",
                uuid,reqPath,notes,str);

        // 获取返回值
        Object proceed = point.proceed();
        // 将返回值转换为json格式
        String jsonData = objectMapper.writeValueAsString(proceed);
        // 结束时间
        long endTime = System.currentTimeMillis();
        // 日志打印
        log.info("CustomLog : {},请求接口地址 : {},接口用途 : {},方法耗时 : {},返回数据JSON格式...{}",
                uuid,reqPath,notes, endTime-startTime, JsonFormatTool.formatJson(jsonData));

        return proceed;
    }
}

编写xml转换工具类
package com.gremlin.utils;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

/** 
 * 封装了XML转换成object,object转换成XML的代码
 * @author gremlin
 */  
public class XmlUtil {
    
    

    /** 
     * 将对象直接转换成String类型的 XML输出
     */  
    public static String convertToXml(Object obj) {
    
      
        // 创建输出流  
        StringWriter sw = new StringWriter();
        sw.append("\n");
        try {
    
      
            // 利用jdk中自带的转换类实现  
            JAXBContext context = JAXBContext.newInstance(obj.getClass());  

            Marshaller marshaller = context.createMarshaller();  
            // 格式化xml输出的格式  
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,  
                    Boolean.TRUE);  
            // 将对象转换成输出流形式的xml  
            marshaller.marshal(obj, sw);  
        } catch (JAXBException e) {
    
      
            e.printStackTrace();  
        }  
        return sw.toString();  
    }  

    /** 
     * 将对象根据路径转换成xml文件
     */
    @SuppressWarnings("unchecked")
    public static void convertToXml(Object obj, String path) {
    
      
        try {
    
      
            // 利用jdk中自带的转换类实现  
            JAXBContext context = JAXBContext.newInstance(obj.getClass());  

            Marshaller marshaller = context.createMarshaller();  
            // 格式化xml输出的格式  
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,  
                    Boolean.TRUE);  
            // 将对象转换成输出流形式的xml  
            // 创建输出流  
            FileWriter fw = null;  
            try {
    
      
                fw = new FileWriter(path);  
            } catch (IOException e) {
    
      
                e.printStackTrace();  
            }  
            marshaller.marshal(obj, fw);  
        } catch (JAXBException e) {
    
      
            e.printStackTrace();  
        }  
    }  

    @SuppressWarnings("unchecked")  
    /** 
     * 将String类型的xml转换成对象 
     */  
    public static Object convertXmlStrToObject(Class<?> clazz, String xmlStr) {
    
    
        Object xmlObject = null;  
        try {
    
      
            JAXBContext context = JAXBContext.newInstance(clazz);  
            // 进行将Xml转成对象的核心接口  
            Unmarshaller unmarshaller = context.createUnmarshaller();  
            StringReader sr = new StringReader(xmlStr);  
            xmlObject = unmarshaller.unmarshal(sr);  
        } catch (JAXBException e) {
    
      
            e.printStackTrace();  
        }  
        return xmlObject;  
    }  

    @SuppressWarnings("unchecked")  
    /** 
     * 将file类型的xml转换成对象 
     */  
    public static Object convertXmlFileToObject(Class clazz, String xmlPath) {
    
      
        Object xmlObject = null;  
        try {
    
      
            JAXBContext context = JAXBContext.newInstance(clazz);  
            Unmarshaller unmarshaller = context.createUnmarshaller();  
            FileReader fr = null;  
            try {
    
      
                fr = new FileReader(xmlPath);  
            } catch (FileNotFoundException e) {
    
      
                e.printStackTrace();  
            }  
            xmlObject = unmarshaller.unmarshal(fr);  
        } catch (JAXBException e) {
    
      
            e.printStackTrace();  
        }  
        return xmlObject;  
    }  
}  

编写格式化json数据格式工具类,用于返回数据格式格式化
package com.gremlin.utils;
 
/**
 * 该类提供格式化JSON字符串的方法。
 * 该类的方法formatJson将JSON字符串格式化,方便查看JSON数据。
 * </p><p>使用算法如下:
 * </p><p>对输入字符串,追个字符的遍历
 * </p><p>1、获取当前字符。
 * </p><p>2、如果当前字符是前方括号、前花括号做如下处理:
 * </p><p>(1)如果前面还有字符,并且字符为“:”,打印:换行和缩进字符字符串。
 * </p><p>(2)打印:当前字符。
 * </p><p>(3)前方括号、前花括号,的后面必须换行。打印:换行。
 * </p><p>(4)每出现一次前方括号、前花括号;缩进次数增加一次。打印:新行缩进。
 * </p><p>(5)进行下一次循环。
 * </p><p>3、如果当前字符是后方括号、后花括号做如下处理:
 * </p><p>(1)后方括号、后花括号,的前面必须换行。打印:换行。
 * </p><p>(2)每出现一次后方括号、后花括号;缩进次数减少一次。打印:缩进。
 * </p><p>(3)打印:当前字符。
 * </p><p>(4)如果当前字符后面还有字符,并且字符不为“,”,打印:换行。
 * </p><p>(5)继续下一次循环。
 * </p><p>4、如果当前字符是逗号。逗号后面换行,并缩进,不改变缩进次数。
 * </p><p>5、打印:当前字符。
 * 
 */
public class JsonFormatTool {
    
    
 
    /**
     * 返回格式化JSON字符串。
     * @param json 未格式化的JSON字符串。
     * @return 格式化的JSON字符串。
     */
    public static String formatJson(String json) {
    
    
        StringBuffer result = new StringBuffer();

        result.append('\n');
        int length = json.length();
        int number = 0;
        char key = 0;
        //遍历输入字符串。
        for (int i = 0; i < length; i++) {
    
    
            //1、获取当前字符。
            key = json.charAt(i);
            //2、如果当前字符是前方括号、前花括号做如下处理:
            if((key == '[') || (key == '{') ) {
    
    
                //(1)如果前面还有字符,并且字符为“:”,打印:换行和缩进字符字符串。
                if((i - 1 > 0) && (json.charAt(i - 1) == ':')) {
    
    
                    result.append('\n');
                    result.append(indent(number));
                }
                //(2)打印:当前字符。
                result.append(key);
                //(3)前方括号、前花括号,的后面必须换行。打印:换行。
                result.append('\n');
                //(4)每出现一次前方括号、前花括号;缩进次数增加一次。打印:新行缩进。
                number++;
                result.append(indent(number));
                //(5)进行下一次循环。
                continue;
            }
 
            //3、如果当前字符是后方括号、后花括号做如下处理:
            if((key == ']') || (key == '}') ) {
    
    
                //(1)后方括号、后花括号,的前面必须换行。打印:换行。
                result.append('\n');
                //(2)每出现一次后方括号、后花括号;缩进次数减少一次。打印:缩进。
                number--;
                result.append(indent(number));
                //(3)打印:当前字符。
                result.append(key);
                //(4)如果当前字符后面还有字符,并且字符不为“,”,打印:换行。
                if(((i + 1) < length) && (json.charAt(i + 1) != ',')) {
    
    
                    result.append('\n');
                }
                //(5)继续下一次循环。
                continue;
            }
 
            //4、如果当前字符是逗号。逗号后面换行,并缩进,不改变缩进次数。
            if((key == ',')) {
    
    
                result.append(key);
                result.append('\n');
                result.append(indent(number));
                continue;
            }
            //5、打印:当前字符。
            result.append(key);
        }
        return result.toString();
    }
 
    /**
     * 返回指定次数的缩进字符串。每一次缩进三个空格,即SPACE。
     * @param number 缩进次数。
     * @return 指定缩进次数的字符串。
     */
    private static String indent(int number) {
    
    
        StringBuffer result = new StringBuffer();
        for(int i = 0; i < number; i++) {
    
    
            result.append("   ");
        }
        return result.toString();
    }
}


编写实体请求类
package com.gremlin.vo.req;

import lombok.Data;
import lombok.experimental.Accessors;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import java.io.Serializable;

/**
 * @className: User
 * @author: gremlin
 * @version: 1.0.0
 * @description: User实体类及请求数据转为xml格式输出日志
 * @date: 2022/8/15 15:57
 */
@Data
@Accessors(chain = true)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "user")
@XmlType(propOrder = {
    
     "id", "name" })
public class User implements Serializable {
    
    

    private static final long serialVersionUID = 1858480386585537937L;

    private Integer id;
    private String name;

}

注:源码请访问https://gitee.com/fantasy-starry/annotation.git

猜你喜欢

转载自blog.csdn.net/rq12345688/article/details/126353649
今日推荐