程式碼精簡執行過程

京东云开发者發表於2024-03-13

一、程式碼精簡背景

隨著業務需求的不斷增加和產品的逐步完善,我們應用對應的程式碼庫也在日益龐大,其中有用的、無用的、低頻使用的、灰度驗證的等各種型別的程式碼堆積在一起,給後續接手的同學增加了很多的維護和學習成本。有些程式碼邏輯缺乏文件說明,無人能看懂,更不敢隨意修改。當有新需求需要改動這些程式碼時,大多數選擇都是重新寫一套,老的程式碼還繼續保留,慢慢的系統中這種程式碼越來越多,程式碼工程也逐漸腐化起來,腐化到一定程度只能進行推倒重構。因此定期進行程式碼縮減,清理腐化的程式碼,對程式碼進行最佳化升級,提升程式碼工程的可維護性與可理解性,是十分有必要的。

我們後端的 java 程式碼工程一般和應用一一對應,根據應用的分層,程式碼工程也可以分為以下幾類:領域服務中心(領域服務)、業務聚合中心(業務系統)、多能力應用(前後端未分離、定時任務、訊息...)等種類。針對不同型別的程式碼庫,我們需要深入程式碼進行探查,掌握程式碼的腐化程度以及有效程式碼的佔比,判斷是進行程式碼縮減還是進行程式碼重構。如果進行程式碼縮減,哪些程式碼可以直接清理,哪些程式碼可以重用最佳化,識別過程是個需要花費精力去認真做的。

二、程式碼精簡步驟





三、程式碼精簡實施

1、靜態程式碼檢查

1)idea 自帶工具檢查

步驟 1:透過 idea 自帶的工具來檢查未使用的類、變數、方法。Preferences—>Analyze—>Inspections,執行:Run Inspection by Name





步驟 2:輸入:unused declaration(未使用的宣告)





步驟 3:輸入:unused import(未使用的引用)





步驟 4:重複程式碼檢查,專業版才能掃描,掃描之後,透過人工識別的方式,看是否能抽離公共方法













使用 idea 自帶工具檢查的缺點如下

◦spring 注入的引數值@value,會認為沒有賦值"Field is never assigned.",引數未被賦值,被檢測出來;

◦lombook 的@data註解物件,會認為"All constructor usages belong to the calls chain that has no members reachable from entry points",被檢測出來;

◦實體類中沒有初始化 new 過,會認為"Constructor is never used",建構函式沒有使用過,被檢測出來;

◦無效程式碼的誤判較多,識別需要要花費一定的時間;

◦重複程式碼檢查都能檢查出來,檢查到以後,都需要人工進行識別,進行抽象處理;

2)使用 PMD 外掛檢查

步驟 1:安裝外掛:IDEA 透過 File > Settings > Plugins > Marketplace 搜尋 “PMD”,按照提示進行安裝,然後重啟即可





步驟 2:配置檢測規則:透過 File > Settings > Other Settings > PMD 可以開啟檢測規則的設定介面





執行 PMD:





使用預設的規則的話,比較多,我們關心的是哪些程式碼可以精簡,因此我們選擇自定義規則,定義 setting.xml 檔案,然後配置 PMD 檢查規則,選擇 setting.xml 檔案。





具體檔案內容如下:

<?xml version="1.0"?>
<ruleset name="myruleset"
         xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
    <description>My ruleset</description>

    <!-- 空的 catch 塊:發現空的 catch 塊沒做任何異常處理的事,在大多數情形下,這會吞噬一些應該被處理或報告的異常 -->
    <rule ref="category/java/errorprone.xml/EmptyCatchBlock"/>
    <!-- 空的 finally 塊:避免空的 finally 塊 - 這些是可以刪掉的 -->
    <rule ref="category/java/errorprone.xml/EmptyFinallyBlock"/>
    <!-- 空的 if 表示式:發現使用 if 進行了條件判斷,但是判斷之後沒做任何處理 -->
    <rule ref="category/java/errorprone.xml/EmptyIfStmt"/>
    <!-- 空的初始化塊:發現空的初始化塊 -->
    <rule ref="category/java/errorprone.xml/EmptyInitializer"/>
    <rule ref="category/java/errorprone.xml/EmptyStatementBlock"/>
    <!-- 空的 switch 表示式:避免空的 switch 表示式 -->
    <rule ref="category/java/errorprone.xml/EmptySwitchStatements"/>
    <!-- 翻譯   空的 Synchronized 塊:避免空的 synchronized 塊 -->
    <rule ref="category/java/errorprone.xml/EmptySynchronizedBlock"/>
    <!-- 空的 try 塊:避免空的 try 塊 -->
    <rule ref="category/java/errorprone.xml/EmptyTryBlock"/>
    <!-- 空的 while 表示式:發現空的 while 表示式 -->
    <rule ref="category/java/errorprone.xml/EmptyWhileStmt"/>

    <!-- 未用的常規引數 -->
<!--    <rule ref="category/java/bestpractices.xml/UnusedFormalParameter"/>-->
    <!-- 未用的本地變數 -->
    <rule ref="category/java/bestpractices.xml/UnusedLocalVariable"/>
    <!-- 未用的私有變數 -->
    <rule ref="category/java/bestpractices.xml/UnusedPrivateField"/>
    <!-- 未用的私有方法 -->
    <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod"/>
    <!-- 不要引入java.lang:避免從’java.lang’包引入任何東西,它裡面的類是自動引入的 -->

    <rule ref="category/java/codestyle.xml/DontImportJavaLang"/>
    <!-- 避免重複引入 -->
    <rule ref="category/java/codestyle.xml/DuplicateImports"/>
    <!-- 未使用的imports:去掉不使用的import -->
    <rule ref="category/java/bestpractices.xml/UnusedImports"/>
    <!-- 從同一個包引入:不需要從同一包引入型別 -->
    <rule ref="category/java/errorprone.xml/ImportFromSamePackage"/>
    <!-- 太多的靜態引入:如果濫用靜態引入特性,會使你的程式不具有可讀性和可維護性,你引入的太多的靜態成員汙染 -->
    <rule ref="category/java/codestyle.xml/TooManyStaticImports"/>

</ruleset>

檢測效果截圖:





步驟 3:重複程式碼檢查,下載 pmd 壓縮包,執行 cpdgui





•命令列執行:/Users/zhanglu7/Downloads/pmd-bin-6.39.0/bin/run.sh cpdgui

•選擇檢查的資料夾目錄,執行檢查,GO





使用 PMD 檢查工具缺點如下:

•缺少無引用的 public 方法檢查規則,目前都是私有方法的檢查,公有方法檢測規則需要自己實現,或者透過人工甄別的方式進行檢查;

•重複程式碼檢查需要額外使用 CPD,重複程式碼檢查都能檢查出來,檢查到以後,都需要人工進行識別,進行抽象處理;

3)靜態程式碼檢查結論

•建議使用 PMD 外掛 +CPD 的方式進行,靜態程式碼檢查,檢查包括:空邏輯控制程式碼塊,未用的本地\私有變數、未使用的私有方法,檢查出來的內容基本上都可以直接從程式碼庫中刪除。重複程式碼檢查,使用 CPD 檢查重複超過 ** 行的程式碼,檢查結果作為一個參考,幫助我們找到程式碼庫中疑似重複的程式碼,然後透過人工甄別,判斷該重複程式碼是否可以進一步抽象,抽象成公共的方法,供多處呼叫。

•test_code_reduce,demo 工程,程式碼行數 103 行,透過 pmd 檢查後,可刪除 10 行左右(包括未使用的私有方法、無效引用、未使用的本地變數),佔比 10% 左右。





•jdo-***,線上工程,程式碼行數 20005,透過 pmd 檢查後,可刪除 400 行左右(包括未使用的私有方法,未使用的私有變數、重複引入、無效引用、未使用的本地變數),佔比 2% 左右。透過 idea 掃描的重複程式碼,需要人工甄別,抽取出公共方法。另外需要人工檢查無用的 public 方法,以及類等,估計可以刪除 600 行左右,佔比 3%,可以完成整體縮減 5% 的目標。





2、動態程式碼檢查

1)京東 jacoco 流水線

步驟 1:配置流水線





步驟 2:行雲應用新增啟動引數

啟動前配置:

mkdir -p /export/home/jacoco

cd /export/home/jacoco && wget "http://storage.jd.local/bpp-quality-public/code_coverage_statistics/jacoco-0.8.7-20210115.151120-42.tar.gz"

tar -xzvf /export/home/jacoco/jacoco-0.8.7-20210115.151120-42.tar.gz





啟動配置:

ip=$(/sbin/ip a | grep "inet " |grep -v "169.254.95.120" |grep -v "127.0.0.1" | awk '{print $2}' | awk -F/ '{print $1}')

export JACOCO_AGENT="-javaagent:/export/home/jacoco/jacoco-0.8.7-20210115.151120-42/lib/jacocoagent.jar=includes=*,output=tcpserver,port=8840,address='${ip}' -Xverify:none"

export CATALINA_OPTS="${JACOCO_AGENT:-} ${CATALINA_OPTS}"





步驟 3:執行流水線





待執行一段時間後,再執行流水線,檢視動態程式碼覆蓋率





2)動態程式碼檢查結論

•建議使用京東 jacoco 流水線來進行動態程式碼檢查,但需要進行長時間的執行才能得到一個相對精準的程式碼執行記錄,該記錄也僅供參考,程式碼是否可以刪除,還需要人工進一步甄別。

相關文章