本文收录在个人博客:www.chengxy-nds.top,技术资源共享。

最近技术部突然刮起一阵 review 代码的小风,挨个项目组过代码,按理说这应该是件挺好的事,让别人指出自己代码中的不足,查缺补漏,对提升自身编码能力有很大帮助,毕竟自己审查很容易“陶醉”在自己写的代码里。

不过,代码 review 的详细程度令人发指,一行一行的分析,简直就是个培训班啊。不夸张的说,如果我村里仅有县重点小学学历的四大爷,来听上一个月后,保证能上手开发,666~

既然组内气氛到这了,咱也得行动起来,要不哪天评审到我的代码,让人家指指点点的心里多少有点不舒服,与其被动优化代码不如主动出击~

选优化代码的方向,方法入参和返回结果日志首当其冲,每个方法都会有这两个日志,一大堆冗余的代码,而且什么样的打印格式都有,非常的杂乱。

public OrderDTO getOrder(OrderVO orderVO, String name) {

        log.info("订单详情入参:orderVO={},name={}", JSON.toJSONString(orderVO), name);

        OrderDTO orderInfo = orderService.getOrderInfo(orderVO);

        log.info("订单详情结果:orderInfo={}", JSON.toJSONString(orderInfo));

        return orderInfo;
}

下边我们利用 AOP 实现请求方法的入参、返回结果日志统一打印,避免日志打印格式杂乱,同时减少业务代码量。

一、自定义注解

自定义切面注解@PrintlnLog 用来输出日志,注解权限 @Target({ElementType.METHOD}) 限制只在方法上使用,注解中只有一个参数 description ,用来自定义方法输出日志的描述。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface PrintlnLog { /**
* 自定义日志描述信息文案
*
* @return
*/
String description() default "";
}

二、切面类

接下来编写@PrintlnLog 注解对应的切面实现,doBefore()中输出方法的自定义描述、入参、请求方式、请求url、被调用方法的位置等信息,doAround() 中打印方法返回结果。

注意: 如何想指定切面在哪个环境执行,可以用@Profile 注解,只打印某个环境的日志。

@Slf4j
@Aspect
@Component
//@Profile({"dev"}) //只对某个环境打印日志
public class LogAspect { private static final String LINE_SEPARATOR = System.lineSeparator(); /**
* 以自定义 @PrintlnLog 注解作为切面入口
*/
@Pointcut("@annotation(com.chengxy.unifiedlog.config.PrintlnLog)")
public void PrintlnLog() {
} /**
* @param joinPoint
* @author fu
* @description 切面方法入参日志打印
* @date 2020/7/15 10:30
*/
@Before("PrintlnLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest(); String methodDetailDescription = this.getAspectMethodLogDescJP(joinPoint); log.info("------------------------------- start --------------------------");
/**
* 打印自定义方法描述
*/
log.info("Method detail Description: {}", methodDetailDescription);
/**
* 打印请求入参
*/
log.info("Request Args: {}", JSON.toJSONString(joinPoint.getArgs()));
/**
* 打印请求方式
*/
log.info("Request method: {}", request.getMethod());
/**
* 打印请求 url
*/
log.info("Request URL: {}", request.getRequestURL().toString()); /**
* 打印调用方法全路径以及执行方法
*/
log.info("Request Class and Method: {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
} /**
* @param proceedingJoinPoint
* @author xiaofu
* @description 切面方法返回结果日志打印
* @date 2020/7/15 10:32
*/
@Around("PrintlnLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { String aspectMethodLogDescPJ = getAspectMethodLogDescPJ(proceedingJoinPoint); long startTime = System.currentTimeMillis(); Object result = proceedingJoinPoint.proceed();
/**
* 输出结果
*/
log.info("{},Response result : {}", aspectMethodLogDescPJ, JSON.toJSONString(result)); /**
* 方法执行耗时
*/
log.info("Time Consuming: {} ms", System.currentTimeMillis() - startTime); return result;
} /**
* @author xiaofu
* @description 切面方法执行后执行
* @date 2020/7/15 10:31
*/
@After("PrintlnLog()")
public void doAfter(JoinPoint joinPoint) throws Throwable {
log.info("------------------------------- End --------------------------" + LINE_SEPARATOR);
} /**
* @param joinPoint
* @author xiaofu
* @description @PrintlnLog 注解作用的切面方法详细细信息
* @date 2020/7/15 10:34
*/
public String getAspectMethodLogDescJP(JoinPoint joinPoint) throws Exception {
String targetName = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Object[] arguments = joinPoint.getArgs();
return getAspectMethodLogDesc(targetName, methodName, arguments);
} /**
* @param proceedingJoinPoint
* @author xiaofu
* @description @PrintlnLog 注解作用的切面方法详细细信息
* @date 2020/7/15 10:34
*/
public String getAspectMethodLogDescPJ(ProceedingJoinPoint proceedingJoinPoint) throws Exception {
String targetName = proceedingJoinPoint.getTarget().getClass().getName();
String methodName = proceedingJoinPoint.getSignature().getName();
Object[] arguments = proceedingJoinPoint.getArgs();
return getAspectMethodLogDesc(targetName, methodName, arguments);
} /**
* @param targetName
* @param methodName
* @param arguments
* @author xiaofu
* @description 自定义注解参数
* @date 2020/7/15 11:51
*/
public String getAspectMethodLogDesc(String targetName, String methodName, Object[] arguments) throws Exception {
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(PrintlnLog.class).description());
break;
}
}
}
return description.toString();
}
}

三、应用

我们在需要打印入参和返回结果日志的方法,加上@PrintlnLog注解,并添加自定义方法描述。

@RestController
@RequestMapping
public class OrderController { @Autowired
private OrderService orderService; @PrintlnLog(description = "订单详情Controller")
@RequestMapping("/order")
public OrderDTO getOrder(OrderVO orderVO, String name) { OrderDTO orderInfo = orderService.getOrderInfo(orderVO); return orderInfo;
}
}

代码里去掉 log.info日志打印,加上 @PrintlnLog 看一下效果,清晰明了。



Demo GitHub地址:https://github.com/chengxy-nds/Springboot-Notebook/tree/master/springboot-aop-unifiedlog

原创不易,燃烧秀发输出内容,如果有一丢丢收获,点个赞鼓励一下吧!

整理了几百本各类技术电子书,送给小伙伴们。关注公号回复【666】自行领取。和一些小伙伴们建了一个技术交流群,一起探讨技术、分享技术资料,旨在共同学习进步,如果感兴趣就加入我们吧!

又被逼着优化代码,这次我干掉了出入参 Log日志的更多相关文章

  1. svn update -r m path 代码还原到某个版本(这样之前的log日志也就没了,也就是清空log日志)

    [root@ok 资料库]# svn log 简历 ------------------------------------------------------------------------ r ...

  2. 为duilib的MenuDemo增加消息响应,优化代码和显示效果

    转载请说明原出处,谢谢~~:http://blog.csdn.net/zhuhongshu/article/details/38253297 第一部分 我在前一段时间研究了怎么制作duilib的菜单, ...

  3. Webpack 4教程:为什么要优化代码

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://wanago.io/2018/07/30/webpack-4-course-part ...

  4. Android 性能优化:使用 Lint 优化代码、去除多余资源

    前言 在保证代码没有功能问题,完成业务开发之余,有追求的程序员还要追求代码的规范.可维护性. 今天,以“成为优秀的程序员”为目标的拭心将和大家一起精益求精,学习使用 Lint 优化我们的代码. 什么是 ...

  5. 如何优化代码中大量的if/else,switch/case?

    前言 随着项目的迭代,代码中存在的分支判断可能会越来越多,当里面涉及到的逻辑比较复杂或者分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗? 正文 使用枚举 这里我们简单 ...

  6. JavaScript工作机制:V8 引擎内部机制及如何编写优化代码的5个诀窍

    概述 JavaScript引擎是一个执行JavaScript代码的程序或解释器.JavaScript引擎可以被实现为标准解释器,或者实现为以某种形式将JavaScript编译为字节码的即时编译器. 下 ...

  7. JavaScript是如何工作的02:深入V8引擎&编写优化代码的5个技巧

    概述 JavaScript引擎是执行 JavaScript 代码的程序或解释器.JavaScript引擎可以实现为标准解释器,或者以某种形式将JavaScript编译为字节码的即时编译器. 以为实现J ...

  8. 通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数--菜单功能'menufile

      通过游戏学python 3.6 第一季 第九章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁 ...

  9. 通过游戏学python 3.6 第一季 第八章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账号--锁定次数

    通过游戏学python 3.6 第一季 第八章 实例项目 猜数字游戏--核心代码--猜测次数--随机函数和屏蔽错误代码--优化代码及注释--简单账号密码登陆--账号的注册查询和密码的找回修改--锁定账 ...

随机推荐

  1. 我从LongAdder中窥探到了高并发的秘籍,上面只写了两个字...

    这是why的第 53 篇原创文章 荒腔走板 大家好,我是why. 时间过的真是快,一周又要结束了.那么,你比上周更博学了吗?先来一个简短的荒腔走板,给冰冷的技术文注入一丝色彩. 上面这图是我之前拼的一 ...

  2. 搜索引擎ElasticSearch入门

    前言 最近项目上需要用到搜索引擎,由于之前自己没有了解过,所以整理了一下搜索引擎的相关概念知识. 正文 想查数据就免不了搜索,搜索就离不开搜索引擎,百度.谷歌都是一个非常庞大复杂的搜索引擎,他们几乎索 ...

  3. SpringCloud gateway 3

    参考博客:https://www.cnblogs.com/crazymakercircle/p/11704077.html 1.1 SpringCloud Gateway 简介 SpringCloud ...

  4. JavaWeb网上图书商城完整项目--day02-20.修改密码各层实现

    1.我们来看看后台操作的业务流程 每一层都按照上面的步骤来进行实现: 这里我们要使用commUtils.toBean把表单提交的参数封装成User对象,必须保证User对象中的字段和表单提交的字段的名 ...

  5. 四层发现-TCP和UDP发现简介

    虽然这里使用到了端口发现,但是四层发现阶段并不对端口进行解析,而是通过端口进行对ip是否存活的判断. 这里是对主机的发现,而不是对端口的识别. 四层发现的结果比三层发现的结果更加精确,基本不会被防火墙 ...

  6. JAVA基础你需要知道的几点

    一.关于变量 变量可以看成可操作的存储空间,有如下三种: 局部变量:定义在方法或语句块内部,必须先声明初始化才能使用:生命周期从声明位置开始到方法或语句块执行完毕. 成员变量(实例变量):定义在方法外 ...

  7. 前端动画必知必会:React 和 Vue 都在用的 FLIP 思想实战

    前言 在 Vue 的官网中的过渡动画章节中,可以看到一个很酷炫的动画效果 乍一看,让我们手写出这个逻辑应该是非常复杂的,先看看本文最后要实现的效果吧,和这个案例是非常类似的. 预览 分析需求 拿到了这 ...

  8. mysql 导入sql脚本中文乱码问题

    1.数据库是否utf8 2.sql文件是否utf8

  9. Win10+YOLOv3完整安装过程(亲测可运行)

    最近了解并尝试在Win10安装YOLOv3,参考了十几篇文章,发现每个人都有自己的安装方式,最初尝试用cmake编译,虽然安装完成,但无法使用GPU,坑非常多,经2天努力终于安装成功,分享并记录自己的 ...

  10. python学习笔记之数据类型(二)

    上一篇博客,小波介绍了python的入门和简单流程控制,这次写python的数据类型和各种数据类型的内置方法. 一.数据类型是何方神圣? 计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当 ...