代码手术刀—自定义你的代码重构工具

京东云开发者
• 阅读 217

前言

笔者近日在做代码仓库的存量代码缩减工作,首先考虑的是基于静态扫描的缩减,尝试使用了很多工具来对代码进行优化,例如PMD、IDEA自带的inspect功能、findBugs等。但是无一例外,要么过于“保守”,只给出扫描结果,但是无法实现一键优化,要么直接就是有bug(这里特指IDEA2023.1.5专业版-inspect功能扫描problems清单里的unused declaration)。对于懒人而言,挨个手动点击几百次按钮和坐牢无异,遂自己写了一个工具对大部分已明确的优化点进行一键修改(具体是使用lombok的@Data注解替换显式的getter/setter以及toString方法)。

本文内容主要分为三个部分,第一部分详细讲述工具实现的思路,第二部分会对用到的开源工具javaParser进行简要的介绍,第三部分提供了工具细致的使用说明。

实现思路

在翻阅历史代码时,发现不少工程仓库里很多类依然是用的IDE生成的getter/setter,如果使用Lombok的@Data注解替换,可以带来几个优点。

•显而易见的是,能够使代码变得更加整洁,减少代码量,并且减少今后新增字段时带来的重复劳动。

•可读性得到了提高,在其他同事参与开发时无需检查getter/setter里是否做了逻辑。

•避免遗漏,减少犯错的风险,之前因为其他同事的接口数据漏写get方法,徒增了不少的沟通成本。

回过头来看,如果我们要写一个工具,对整个代码工程所有类进行全量扫描,并且使用lombok来替换其中的“没有特殊逻辑”的getter和setter,需要哪些步骤。

1.扫描整个工程代码,可以是多模块的工程。

2.读取其中的“.java”文件。

3.过滤其中不需要的类,例如interface,没有field的类(大概率是作为service出现),注解的声明等等。

4.删除getter/setter方法,这里需要判断在get和set方法里是否有特殊逻辑。

5.给类打上@Data注解,并且把lombok包引入进来。

6.把修改后的内容写入java文件。

下面对每个步骤的实现进行说明。

工程扫描

工程扫描比较简单,给一个工程路径,然后递归调用,过滤出所有的.java文件即可。

    private static List<File> scanJavaFiles(File file) {
        List<File> result = Lists.newArrayList();
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files == null) {
                return result;
            }
            for (File f : files) {
                result.addAll(scanJavaFiles(f));
            }
        }
        if (file.getName().endsWith(".java")) {
            result.add(file);
        }
        return result;
    }

使用注解替换getter/setter

在拿到所有文件的列表之后,需要对其进行处理。

1.过滤掉无字段的类。

2.过滤掉已经使用lombok注解的类。

3.判断是否有显式getter/setter(这里需要注意,boolean类型的字段需要特殊处理)

4.判断getter/setter是否为简单的返回和赋值操作。

5.删除getter/setter。

6.添加@Data注解。

7.添加lombok包的引入。

这里使用github上开源的工具javaParser来对类进行解析、代码提取、删除以及内容新增,javaParser会在下一章节进行介绍。



代码手术刀—自定义你的代码重构工具





这里简单粗暴了一些,直接使用equals判断方法体,其实javaParser提供了更完善的api来分析语义。





代码手术刀—自定义你的代码重构工具



更新java文件

在完成对代码的清理之后,需要将内容更新到java文件,CompilationUnit重写了toString方法,可以支持直接将代码转换成字符串的形式。





代码手术刀—自定义你的代码重构工具



JavaParser介绍

JavaParser是什么?

JavaParser 是一个开源的 Java 源代码分析工具,它提供了一系列简单的API来解析、修改和生成 Java 代码。



代码手术刀—自定义你的代码重构工具



举个例子,我们可以使用javaparser轻松的实现下面几个操作:

1.分析代码中的类、方法、字段等元素,提取类的继承关系、方法的参数和返回类型等。

2.更改源码,例如重命名方法、修改方法体、添加或删除代码行等。

3.可以使用它来生成代码片段,例如创建新的类、方法或字段,或者生成代码文档。

在上一章节里就用到了数据提取,源码替换功能。这里附上JavaParser的相关链接:

官网:https://javaparser.org/ github:https://github.com/javaparser/javaparser wiki:https://github.com/javaparser/javaparser.wiki.git javadoc:https://www.javadoc.io/doc/com.github.javaparser/javaparser-core/latest/index.html



核心组件

JavaParser 的主要构成由以下几个组件组成:

1.Lexer(词法分析器):词法分析器的作用是读取源代码文本,并将其分解成一系列的词法单元(tokens),如关键字、标识符、字面量、运算符等。这是解析过程的第一步。



代码手术刀—自定义你的代码重构工具



通常不需要咱们显式调用,JavaParser将具体的细节实现隐藏在内部,调用方只需要使用开放api即可完成源码到AST的转换。具体可以翻阅com.github.javaparser.GeneratedJavaParser



  1. Parser(语法解析器):语法分析器接收词法分析器生成的tokens,并根据Java语言的语法规则将它们组合成各种语法结构,如表达式、语句、类定义等。这个过程构建出一个抽象语法树(AST)。

com.github.javaparser.JavaParser这是最常用的类,用于触发解析过程并生成AST,在上一章节中,使用StaticJavaParser将源文件解析成CompilationUnit,在parse方法的内部使用了JavaParser完成这一解析过程。

  1. AST(抽象语法树):AST 是 JavaParser 的核心数据结构,它以层次化的方式表示了源代码的结构。AST 由一系列的节点组成,每个节点表示源代码中的一个元素,如类、方法、字段、表达式等。每个节点都包含有关该元素的信息,例如名称、类型、修饰符等。

AST是后续操作(如遍历、分析、修改)的基础,也是使用方操作最频繁的类。上一章节使用的com.github.javaparser.ast.CompilationUnit是一个非常重要的类,它代表了Java源代码文件的根节点,是这个结构的抽象表示,包含整个文件的结构,例如:

• 包声明(Package Declaration)

•导入声明(Imports)

•类型声明(Type Declarations),这可能是类、接口、枚举或注解

•注释(Comments)

•任何顶级的注解

通过操作CompilationUnit提供的公有方法,可以访问和修改文件中的元素。包括:

•获取和设置包声明

•获取和添加导入声明

•获取和添加类型声明

•获取和添加注释

•使用访问者模式来遍历AST中的节点



代码手术刀—自定义你的代码重构工具



  1. Visitors(访问器):顾名思义,这是一个采用访问者模式设计的组件,可以用于遍历和操作 AST 。开发者可以编写自定义的 Visitors,通过遍历 AST 来访问特定类型的节点,执行代码分析、重构、生成等任务。

com.github.javaparser.ast.visitor.GenericVisitorcom.github.javaparser.ast.visitor.VoidVisitor这两个访问器提供了默认实现,如果需要自定义访问器,可以继承它们来实现自己的业务逻辑。

  1. Printer(打印器):这个也很好理解,Printer 用于将 AST 转换回 Java 源代码的字符串表示形式。它可以将修改后的 AST 打印回原始源代码文件,或将 AST 打印为格式化的代码字符串。在上一章节的最后提到的CompilationUnit重写的toString方法,实际上就是使用了Printer来完成AST到源码字符串的转换。

上面的一些组件是javaParser中比较核心且常用的部分,当然javaParser还提供了一些便捷的工具类以及用法,这些内容笔者也没有接触过,有需要的读者可以自行翻阅文档。



工具使用方式

第一章提到的jar包和源码均已上传私服,可以直接通过maven插件的方式运行。

添加依赖

        <dependency>
            <groupId>com.jd.omni.opdd</groupId>
            <artifactId>lombok-replace</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

添加maven插件

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.jd.omni.opdd.tools.lombok.LombokConverter</mainClass>
                    <arguments>
                        <argument>../../pop-jingme-customs</argument>
                    </arguments>
                </configuration>
            </plugin>

插件中的argument节点需要替换成工程的路径,可以是绝对路径也可以是相对路径

运行插件

执行 mvn exec:java





代码手术刀—自定义你的代码重构工具





可以在控制台看到:



代码手术刀—自定义你的代码重构工具



注意事项

使用工具处理完成后,一定要build的一下检查是否有编译错误,虽然在删除操作时做了较为严苛的校验,但是有些特殊的变量名可能没有考虑到,这部分问题是可以通过编译检查出来的。

另外,对于没有引用lombok的模块,需要手动添加依赖。

写在最后

代码重构应该像手术刀一样,快、准、狠,正所谓君子生非异也,善假于物也。本文主要起一个抛砖引玉的作用,重点在于JavaParser的介绍,笔者写的这个小工具非常简单,之前也写过B-PaaS一键生成matrix.json,一键根据错误码定义生成i18n文件,大都不难。

点赞
收藏
评论区
推荐文章
雷厉风行 雷厉风行
1年前
Mac上最优秀的Ruby开发工具:JetBrains RubyMine 2023 永久激活码
JetBrainsRubyMine2023forMac是一款专业的Ruby开发工具,旨在使开发人员的工作更加高效和愉悦。这款软件提供了一系列强大的功能,包括智能代码补全、代码重构、调试和多种工具等。
Easter79 Easter79
3年前
SublimeText3系列(2)
1.什么是linter?linter是用来检查代码是否符合规则的工具,例如检查Javascript代码的jshint工具,你可以设置其规则"eqeqeq":true,那么如果在你的代码文件中使用了而不是,那么检查的时候就会给出提示。2.什么是SublimeLinterSub
Stella981 Stella981
3年前
DevOps简单介绍
jenkins作为测试环境代码发布工具,sonar作为静态代码检查工具,idea作为开发工具,jira作为缺陷管理平台,upstream作为codereview工具(正在研究)。DevOps最近比较火热,但是核心貌似都是jenkinsDevOps是一个完整的面向IT运维的工作流,以IT自动化以及持续集成(CI)、持续部署(CD)为基础,来优化程式
Stella981 Stella981
3年前
GitHub 官方代码扫描工具上线!
点击上方Java后端,选择设为星标优质项目,及时送达作者:蛋酱;本文经机器之心(微信公众号:almosthuman2014)授权转载,禁止二次转载。在GitHub发布项目之前,你可以用免费的官方代码扫描程序来检查Bug了。!(https://oscimg.oschina.net/oscnet/3cac
Easter79 Easter79
3年前
Spring第七篇【Spring的JDBC模块】
前言上一篇Spring博文主要讲解了如何使用Spring来实现AOP编程,本博文主要讲解Spring的对JDBC的支持…对于JDBC而言,我们肯定不会陌生,我们在初学的时候肯定写过非常非常多的JDBC模板代码!回顾对模版代码优化过程我们来回忆一下我们怎么对模板代码进行优化的!首先来看一
Stella981 Stella981
3年前
53w字!阿里首推系统性能优化指南太香了,堪称性能优化最优解
性能优化我们都知道代码是系统的基石,没有良好的代码,系统架构就不牢固。但作为一般一个普通程序员,很少有机会参与系统架构级别的优化,甚至暂时不能理解架构上的调整。在开发新功能或审查组内的代码时,优化系统的方式主要是优化自己或他人写的代码。但是真实的情况是:且不说其他层次的优化,就一个代码优化很多入行没有多久的小伙伴甚至都还没入门,更别说啥实现
新支点小玉 新支点小玉
10个月前
CNAS中兴新支点——源代码审计对企业有哪些好处
源代码扫描,对应用程序进行静态漏洞扫描,分析源代码中存在的安全风险,运行应用于模拟器中对应用进行实时漏洞攻击检测。你是否了解源代码扫描对企业的好处?一、源代码扫描,通常能够帮助企业解决这些问题:1、软件代码中安全漏洞和未声明功能的存在是信息安全事件频繁发生
京东云开发者 京东云开发者
9个月前
代码手术刀-自定义你的代码重构工具
前言笔者近日在做代码仓库的存量代码缩减工作,首先考虑的是基于静态扫描的缩减,尝试使用了很多工具来对代码进行优化,例如PMD、IDEA自带的inspect功能、findBugs等。但是无一例外,要么过于“保守”,只给出扫描结果,但是无法实现一键优化,要么直接
京东云开发者 京东云开发者
8个月前
20年编程,AI编程6个月,关于Copliot辅助编码工具,你想知道的都在这里
AI代码辅助工具尝试各种辅助编程的AI工具笔者是一个后端Coder,开发工具使用Idea和VsCode。在过去我一直尝试找到一款适合自己的智能代码辅助工具,来告别繁琐的重复性编码,好提高开发效率。直到AIGC和AIAgent的迅速发展,越来越多的AI编码辅
京东云开发者 京东云开发者
7个月前
无用代码扫描组件设计
1、现状痛点系统越来越臃肿,开发过程中可能产生的无用代码增加了系统维护成本。2、设计思路2.1、静态代码扫描方案本方案解决静态代码下无调用方法扫描,通过ASTParser对静态文件进行扫描分析,获取代码块来判断调用关系。基本步骤及思路(1)载入本地磁盘项目