Bootstrap

AOP开发应用与分析:方法自动打印出入参举例

学习Spring AOP理解与研发使用_aop开发-CSDN博客总结应用

一、项目介绍

平时的开发中有些特定的接口为了方便后续定位问题,往往需要打印方法的出入参数以及本次处理所消耗的时间信息等内容,在该背景下,我们可以增加切面对于指定的方法进行处理前的参数打印以及返回前的参数结果打印的处理,同时可以去指定机器或环境来打印等基本控制手段,具体流程图如下。

二、方法日志打印注解

package org.zyf.javabasic.aop.printlog;
 
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 yanfengzhang
 * @description 方法日志打印信息处理
 * @date 2022/6/13  23:54
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PrintLogInfo {
    /**
     * 自定义日志描述信息文案
     */
    String description() default "";
}

三、切面定义

package org.zyf.javabasic.aop.printlog;
 
import com.alibaba.fastjson.JSON;
import com.sankuai.inf.octo.mns.model.HostEnv;
import com.sankuai.inf.octo.mns.util.ProcessInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.zyf.javabasic.config.SwitchCommonConfig;
 
import java.lang.reflect.Method;
import java.util.Objects;
 
/**
 * @author yanfengzhang
 * @description
 * @date 2022/6/15  23:02
 */
@Slf4j
@Aspect
@Component
public class PrintLogInfoAspet {
 
    StopWatch watch = new StopWatch();
 
    /**
     * 方法以 @PrintLogInfo 注解作为切面入口
     */
    @Pointcut("@annotation(org.zyf.javabasic.aop.printlog.PrintLogInfo)")
    public void printBasicLog() {
 
    }
 
    /**
     * 方法入参前打印对应的入参信息内容
     *
     * @param joinPoint 切入点信息
     * @throws Throwable 异常信息
     */
    @Before("printBasicLog()")
    public void doBefore(JoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        /*1.判断是否需要开启打印日志功能*/
        if (!isPrintLog()) {
            return;
        }
 
        /*2.从方法上提取当前配置的注解信息*/
        PrintLogInfo printLogInfo = targetMethod.getAnnotation(PrintLogInfo.class);
        String description = printLogInfo.description();
        if (StringUtils.isBlank(description)) {
            description = "未表明归属";
        }
 
        /*3.打印相关入参参数*/
        StringBuilder methodInfo = new StringBuilder();
        methodInfo.append(methodSignature.getDeclaringTypeName()).append(".").append(targetMethod.getName());
        watch.start(methodInfo.toString());
        StringBuilder logRequestInfo = new StringBuilder();
        logRequestInfo.append("\n").append("请求方法:").append(methodInfo).append("\n");
        logRequestInfo.append("方法日志描述信息:").append(description).append("\n");
        logRequestInfo.append("入参信息:").append(JSON.toJSONString(joinPoint.getArgs()));
        log.info(logRequestInfo.toString());
    }
 
    /**
     * 方法返回之前执行,打印才返回值以及方法消耗时间
     *
     * @param response 返回值
     */
    @AfterReturning(returning = "response", pointcut = "printBasicLog()")
    public void doAfterRunning(Object response) {
        /*1.判断是否需要开启打印日志功能*/
        if (!isPrintLog()) {
            return;
        }
 
        /*2.从方法上提取当前配置的注解信息*/
        StringBuilder logResponseInfo = new StringBuilder();
        logResponseInfo.append("\n").append("出参信息:").append(JSON.toJSONString(response)).append("\n");
        logResponseInfo.append("本次方法执行时间耗时:").append(watch.getTotalTimeMillis()).append("ms");
        log.info(logResponseInfo.toString());
    }
 
    /**
     * 日志打印生效环境和机器提取提取,用于测试指定环境和机器来定位问题
     *
     * @return false-默认不进行打印
     */
    private boolean isPrintLog() {
        /*1.全部全部统一生效或失效开关*/
        if (SwitchCommonConfig.openPrintLog()) {
            return true;
        }
 
        /*2.按生效条件分析是否打印*/
        HostEnv hostEnv = ProcessInfoUtil.getHostEnv();
        String localIpV4 = ProcessInfoUtil.getLocalIpV4();
 
        /*2.1没有配置默认不打印相关数据*/
        if (Objects.isNull(hostEnv) && StringUtils.isBlank(localIpV4)) {
            return false;
        }
 
        /*2.2如果配置了指定机器打印的话,优先对机器执行打印*/
        if (SwitchCommonConfig.getIpsForPrintLog().contains(localIpV4)) {
            return true;
        }
 
        /*2.3当机器没有生效的时候,看生效环境*/
        if (SwitchCommonConfig.getEnvsForPrintLog().contains(hostEnv)) {
            return true;
        }
        return false;
    }
}

四、业务应用

(一)基本数据准备

package org.zyf.javabasic.aop.printlog;
 
import lombok.Builder;
import lombok.Data;
 
/**
 * @author yanfengzhang
 * @description
 * @date 2022/7/13  23:56
 */
@Data
@Builder
public class ZyfInfoRequest {
    /**
     * 基本信息
     */
    private boolean needBasicInfo;
    /**
     * 博客信息
     */
    private boolean needBlogInfo;
}

package org.zyf.javabasic.aop.printlog;
 
import lombok.Builder;
import lombok.Data;
 
import java.util.List;
 
/**
 * @author yanfengzhang
 * @description
 * @date 2022/7/13  23:56
 */
@Data
@Builder
public class ZyfInfoResponse {
    /**
     * 姓名
     */
    private String name;
    /**
     * 年纪
     */
    private int age;
    /**
     * 身高:cm
     */
    private float height;
    /**
     * 体重:kg
     */
    private float weight;
    /**
     * 博客信息展示
     */
    private List<String> blogShowcases;
}

具体服务

 
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
 
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
/**
 * @author yanfengzhang
 * @description
 * @date 2022/7/13  23:55
 */
@Service
@Slf4j
public class ZyfInfoService {
    @PrintLogInfo(description = "获取张彦峰的相关信息")
    public ZyfInfoResponse getZyfInfo(ZyfInfoRequest zyfInfoRequest) {
        if (zyfInfoRequest.isNeedBasicInfo() && !zyfInfoRequest.isNeedBlogInfo()) {
            return ZyfInfoResponse.builder()
                    .name("张彦峰")
                    .age(29)
                    .height(185)
                    .weight(83).build();
        }
 
        List<String> blogShowcases = Stream.of("https://blog.csdn.net/xiaofeng10330111/article/details/106086200",
                "https://blog.csdn.net/xiaofeng10330111/article/details/106086035").collect(Collectors.toList());
 
        log.info("获取张彦峰的相关信息业务处理完成");
        return ZyfInfoResponse.builder()
                .name("张彦峰")
                .age(29)
                .height(185)
                .weight(83)
                .blogShowcases(blogShowcases).build();
    }
}

测试结果
基本测试代码

package org.zyf.javabasic.aop.printlog;
 
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zyf.javabasic.ZYFApplication;
 
/**
 * @author yanfengzhang
 * @description
 * @date 2022/7/13  23:49
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZYFApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public class ZyfInfoServiceTest {
    @Autowired
    private ZyfInfoService zyfInfoService;
 
    @Test
    public void test() {
        zyfInfoService.getZyfInfo(ZyfInfoRequest.builder().needBlogInfo(true).needBasicInfo(true).build());
    }
}

测试结果展示

(二)mock赋能干预举例
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/xiaofeng10330111/article/details/106086455

;