代碼手術刀—自定義你的代碼重構工具

Tech




01  
前言


在今年的敏捷團隊建設中,我通過Suite執行器實現了一鍵自動化單元測試。Juint除了Suite執行器還有哪些執行器呢?由此我的Runner探索之旅開始了!
筆者 近日在做代碼倉庫的存量代碼縮減工作,首先考慮的是基於靜態掃描的縮減,嘗試使用了很多工具來對代碼進行優化,例如PMD、IDEA自帶的inspect功能、findBugs等。 但是無一例外,要麼過於“保守”,只給出掃描結果,但是無法實現一鍵優化,要麼直接就是有bug(這裏特指IDEA2023.1.5專業版-inspect功能掃描problems清單裏的unused declaration)。 對於懶人而言,挨個手動點擊幾百次按鈕和坐牢無異,遂自己寫了一個工具對大部分已明確的優化點進行一鍵修改(具體是使用lombok的@Data註解替換顯式的getter/setter以及toString方法)。
本文內容主要分爲三個部分,第一部分詳細講述工具實現的思路,第二部分會對用到的開源工具javaParser進行簡要的介紹,第三部分提供了工具細緻的使用說明。




02  
  實現思路  


理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。
在翻閱歷史代碼時,發現不少工程倉庫裏很多類依然是用的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方法,可以支持直接將代碼轉換成字符串的形式。



03  
  JavaParse介紹  


理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。

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
2. Parser(語法解析器):語法分析器接收詞法分析器生成的tokens,並根據Java語言的語法規則將它們組合成各種語法結構,如表達式、語句、類定義等。這個過程構建出一個抽象語法樹(AST)。
com.github.javaparser.JavaParser 這是最常用的類,用於觸發解析過程並生成AST,在上一章節中,使用StaticJavaParser將源文件解析成CompilationUnit,在parse方法的內部使用了JavaParser完成這一解析過程。
3. AST(抽象語法樹):AST 是 JavaParser 的核心數據結構,它以層次化的方式表示了源代碼的結構。AST 由一系列的節點組成,每個節點表示源代碼中的一個元素,如類、方法、字段、表達式等。每個節點都包含有關該元素的信息,例如名稱、類型、修飾符等。
AST是後續操作(如遍歷、分析、修改)的基礎,也是使用方操作最頻繁的類。上一章節使用的com.github.javaparser.ast.CompilationUnit是一個非常重要的類,它代表了Java源代碼文件的根節點,是這個結構的抽象表示,包含整個文件的結構,例如:
  • 包聲明(Package Declaration)

  • 導入聲明(Imports)

  • 類型聲明(Type Declarations),這可能是類、接口、枚舉或註解

  • 註釋(Comments)

  • 任何頂級的註解
通過操作CompilationUnit提供的公有方法,可以訪問和修改文件中的元素。包括:
  • 獲取和設置包聲明

  • 獲取和添加導入聲明

  • 獲取和添加類型聲明

  • 獲取和添加註釋

  • 使用訪問者模式來遍歷AST中的節點


4. Visitors(訪問器):顧名思義,這是一個採用訪問者模式設計的組件,可以用於遍歷和操作 AST 。開發者可以編寫自定義的 Visitors,通過遍歷 AST 來訪問特定類型的節點,執行代碼分析、重構、生成等任務。
com.github.javaparser.ast.visitor.GenericVisitor和com.github.javaparser.ast.visitor.VoidVisitor這兩個訪問器提供了默認實現,如果需要自定義訪問器,可以繼承它們來實現自己的業務邏輯。
5. Printer(打印器):這個也很好理解,Printer 用於將 AST 轉換回 Java 源代碼的字符串表示形式。它可以將修改後的 AST 打印回原始源代碼文件,或將 AST 打印爲格式化的代碼字符串。在上一章節的最後提到的CompilationUnit重寫的toString方法,實際上就是使用了Printer來完成AST到源碼字符串的轉換。

上面的一些組件是javaParser中比較核心且常用的部分,當然javaParser還提供了一些便捷的工具類以及用法,這些內容筆者也沒有接觸過,有需要的讀者可以自行翻閱文檔。



04  
  工具使用方式  


理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。     
第一章提到的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的模塊,需要手動添加依賴。



05  
  寫在最後  


理解,首先 MCube 會依據模板緩存狀態判斷是否需要網絡獲取最新模板,當獲取到模板後進行模板加載,加載階段會將產物轉換爲視圖樹的結構,轉換完成後將通過表達式引擎解析表達式並取得正確的值,通過事件解析引擎解析用戶自定義事件並完成事件的綁定,完成解析賦值以及事件綁定後進行視圖的渲染,最終將目標頁面展示到屏幕。     

代碼重構應該像手術刀一樣,快、準、狠,正所謂君子生非異也,善假於物也。本文主要起一個拋磚引玉的作用,重點在於JavaParser的介紹,筆者寫的這個小工具非常簡單,之前也寫過B-PaaS一鍵生成matrix.json,一鍵根據錯誤碼定義生成i18n文件,大都不難。


本文分享自微信公衆號 - 京東雲開發者(JDT_Developers)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章