別再寫垃圾代碼了:試試阿裡巴巴 Java 開發插件,打造你的團隊專屬風格。。。

来源:https://www.cnblogs.com/javastack/archive/2022/03/26/16059124.html
-Advertisement-
Play Games

1.前言 工作中難免會遇到維護別人代碼的情況,那麼首先就得看懂別人寫的代碼。如果對方寫的代碼混亂臃腫,維護成本必然很高,如果對方寫的代碼優雅清晰,那維護的人看起來必然心情愉悅。正所謂“前人栽樹,後人乘涼;前人埋坑,後人罵娘”。 代碼首先是給人看的,其次才是給機器看到,如何編寫出任何人都看到懂的代碼? ...


1.前言

工作中難免會遇到維護別人代碼的情況,那麼首先就得看懂別人寫的代碼。如果對方寫的代碼混亂臃腫,維護成本必然很高,如果對方寫的代碼優雅清晰,那維護的人看起來必然心情愉悅。正所謂“前人栽樹,後人乘涼;前人埋坑,後人罵娘”。

代碼首先是給人看的,其次才是給機器看到,如何編寫出任何人都看到懂的代碼?答案是制定規範!

每個公司都會有自己的編碼規範,但是往往的情況是趕項目進度或者懶惰或者個人水平習慣等原因,加上沒有code review,最後代碼就寫的千奇百怪了。原因就在於規範是有了,但是沒人遵守。所以,編碼規範需要強制執行,交給工具來強制執行。

本文將通過介紹java靜態代碼檢查工具PMD、阿裡巴巴p3c開源項目到最後編寫自定義編碼規約來學習如何規範代碼的編寫。

2.PMD靜態代碼掃描

2.1.PMD官網

https://pmd.github.io/

2.2.概述

PMD是一種開源分析Java代碼錯誤的工具。它通過靜態分析獲知代碼錯誤。也就是說,在不運行Java程式的情況下報告錯誤。PMD附帶了許多可以直接使用的規則,利用這些規則可以找出Java源程式的許多問題,例如:

  • 潛在的bug:空的try/catch/finally/switch語句
  • 未使用的代碼:未使用的局部變數、參數、私有方法等
  • 可選的代碼:String/StringBuffer的濫用
  • 複雜的表達式:不必須的if語句、可以使用while迴圈完成的for迴圈
  • 重覆的代碼:拷貝/粘貼代碼意味著拷貝/粘貼bugs
  • 迴圈體創建新對象:儘量不要再for或while迴圈體內實例化一個新對象
  • 資源關閉:Connect,Result,Statement等使用之後確保關閉掉

此外,用戶還可以自己定義規則,檢查Java代碼是否符合某些特定的編碼規範。例如,你可以編寫一個規則,要求PMD找出所有創建Thread和Socket對象的操作。

2.3.工作原理

PMD的核心是JavaCC解析器生成器。PMD結合運用JavaCC和EBNF(擴展巴科斯-諾爾範式,Extended Backus-Naur Formal)語法,再加上JJTree,把Java源代碼解析成抽象語法樹(AST,Abstract Syntax Tree)

從根本上看,Java源代碼只是一些普通的文本。不過,為了讓解析器承認 這些普通的文本是合法的Java代碼,它們必須符合某種特定的結構要求。這種結構可以用一種稱為EBNF的句法元語言表示,通常稱為“語法” (Grammar)。JavaCC根據語法要求生成解析器,這個解析器就可以用於解析用Java編程語言編寫的程式。

2.4.規則分類

  • 最佳實踐:公認的最佳實踐的規則。
  • 代碼風格:這些規則強制執行特定的編碼風格。
  • 設計:幫助您發現設計問題的規則。
  • 文檔:這些規則與代碼文檔有關。
  • 容易出錯的規則:用於檢測被破壞的、非常混亂的或容易發生運行時錯誤的結構的規則。
  • 多線程:這些規則在處理多個執行線程時標記問題。
  • 性能:標記存在性能問題的代碼的規則。
  • 安全:顯示潛在安全缺陷的規則。

2.5.編寫PMD自定義規則

3.阿裡巴巴Java開發規約插件p3c

3.1.GITHUB地址

https://github.com/alibaba/p3c

3.2.概述

阿裡巴巴p3c項目包含三個部分:

  • p3c-pmd,提供大部分規則實現,基於PMD框架開發,如果想實現自己的規則,可以基於該模塊開發(該模塊基於maven編譯打包)
  • IntelliJ IDEA插件,即idea-plugin模塊(該模塊基於gradle編譯打包)
  • Eclipse插件,即eclipse-plugin,本文不介紹

3.3.阿裡編碼規約IDEA插件使用

傳送門:https://github.com/alibaba/p3c/wiki/IDEA插件使用文檔

4.基於p3c編寫自定義編碼規則

4.1.自定義規則

假設現在需要開發這麼一個規則:方法請求參數列表不允許超過(含)5個

4.2.開發步驟

4.2.1.找出問題代碼,使用pmd圖形化工具解析成抽象語法樹

代碼示例:

package org.p3c.demo;

public class Demo {

    public void methodA(int a) {

    }

    public void methodB(int a, int b, int c, int d, int e) {

    }
}

將源碼放入Source Code框,點擊Go按鈕,解析結果顯示在左下框

4.2.2.分析抽象語法樹

可以看到,整棵樹的根節點是CompilationUnit,即編譯單元,代表每個java源文件。我們首先要找到所有的方法聲明,根據樹節點名稱大概也能看出來是MethodDeclaration,點擊相應的節點,看看游標是否定位到源碼方法聲明位置。

仔細分析MethodDeclaration節點,可以看到他有以下幾個直接子節點:ResultType、MethodDeclarator、Block,即返回類型、方法聲明、方法體

MethodDeclarator是我們想找的節點XPATH表達式可以這麼寫:

//CompilationUnit//MethodDeclarator

驗證表達式是否正確,將它寫到PMD圖形界面XPATH Query框中,點擊Go按鈕

接下來,我們需要找到每個方法對應的參數列表,參數列表節點是方法節點的直接子節點,完整XPATH表達式為:

//CompilationUnit//MethodDeclarator/FormalParameters

獲取到參數列表節點後,我們查看該節點的屬性,找出參數個數的屬性,觀察可以發現是ParameterCount屬性。

到現在為止,抽象語法樹已經分析完,我們知道這麼找出代碼中參數列表大於等於5個的方法了。

4.2.3.p3c-pmd項目編寫自定義代碼規則

打開阿裡p3c-pmd工程,開始編寫我們的自定義規則。

阿裡已經寫了很多規則,我們現在要編寫的規則屬於面向對象範疇,可以把規則寫到opp包下,新建一個規則類MethodParameterCountRule,繼承自AbstractAliRule,重寫 visit方法:

package com.alibaba.p3c.pmd.lang.java.rule.oop;

import com.alibaba.p3c.pmd.lang.java.rule.AbstractAliRule;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters;

import java.util.List;

/**
 * 方法參數列表個數不宜過長
 *
 * @auther qingjian.wu
 * @create 2018-01-27 14:59
 */
public class MethodParameterCountRule extends AbstractAliRule{

    private static final String METHOD_XPATH = "//MethodDeclarator";

   private static final Integer PARAMETER_COUNT_LIMIT = 5;

    @Override
    public Object visit(ASTCompilationUnit node, Object data) {
        try {
            // 找到所方法節點
            List<Node> methodNodes = node.findChildNodesWithXPath(METHOD_XPATH);
            if (methodNodes != null && methodNodes.size() > 0) {
                for (Node methodNode : methodNodes) {
                    // 找到每個方法的參數列表聲明
                    List<ASTFormalParameters> formalParameters = methodNode.findChildrenOfType(ASTFormalParameters.class);
                    if (formalParameters.get(0).getParameterCount() >= PARAMETER_COUNT_LIMIT) {
                        // 違反規則提示信息,第二個參數是提示信息位置,第三個參數是提示信息key,第四個參數用來替換提示信息
                        // 中的占位符,這裡獲取的節點image屬性就是方法名稱
                        addViolationWithMessage(data, methodNode,
                                "java.oop.MethodParameterCountRule.violation.msg",
                                new Object[]{methodNode.getImage()});
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.visit(node, data);
    }
}

4.2.4.p3c-pmd項目配置規則

將編寫好規則配置到ali-oop.xml文件中

    <rule name="MethodParameterCountRule"
          language="java"
          message="java.oop.MethodParameterCountRule.rule.msg"
          class="com.alibaba.p3c.pmd.lang.java.rule.oop.MethodParameterCountRule">
        <!--級別,1強制,2推薦,3參考-->
        <priority>1</priority>

        <example>
            <![CDATA[
Negative example:
    public void methodB(int a, int b, int c, int d, int e) {

    }
]]>
        </example>
        <example>
            <![CDATA[
Positive example:
    public void methodA() {

    }
]]>
        </example>
    </rule>

4.2.5.p3c-pmd項目編寫提示信息

上兩步使用的提示信息和規則信息需要編寫到message.xml配置文件中,message_en.xml中是英文提示,這裡就先不演示了

<entry key="java.oop.MethodParameterCountRule.violation.msg">
    <![CDATA[方法【%s】參數列表過長。 ]]>
</entry>
<entry key="java.oop.MethodParameterCountRule.rule.msg">
    <![CDATA[說明:方法參數列表不允許超過(含)5個,建議封裝到一個對象中。]]>
</entry>

4.2.6.單元測試

編寫測試樣例,將要測試的源代碼寫到test目錄對應的xml文件中

推薦一個 Spring Boot 基礎教程及實戰示例:
https://github.com/javastacks/spring-boot-best-practice

<?xml version="1.0" encoding="UTF-8"?>
<test-data>

 <code-fragment id="測試樣例">
  <![CDATA[
 package org.p3c.demo;

 public class Demo {

  public void methodA(int a) {

  }

  public void methodB(int a, int b, int c, int d, int e) {

  }
 }
  ]]>
 </code-fragment>
 <test-code>
  <!-- 預期問題個數 -->
  <expected-problems>0</expected-problems>
  <code-ref id="測試樣例" />
 </test-code>
</test-data>

編寫單元測試

運行單元測試,因為樣例代碼中methodB不符合規範,但是我們預期問題個數寫的是0,所以單元測試會不通過:

4.3.配置插件

4.3.1.p3c-pmd打包安裝到本地maven倉庫

先把用不到的插件maven-javadoc-plugin和maven-gpg-plugin註釋掉,然後運行mvn命令:

mvn -DskipTests=true clean install

4.3.2.idea-plugin項目打包插件

idea-plugin項目基於gradle構建,配置根目錄下build.gradle,讓構建使用本地私有maven倉庫構建

然後運行開始gradle構建:

clean buildDependents build

打包成功後會在idea-plugin\p3c-idea\build\distributions\目錄下生成Alibaba Java Coding Guidelines-1.0.0.zip文件,這個就是我們加入了自己拓展阿裡開發規約的插件,IDEA中安裝此插件

Settings->Plugins->Install plugin from disk

4.3.3.IDEA中使用編碼規約插件

安裝完插件重啟IDEA,用之前的代碼測試下插件是否生效。

右鍵點擊“編碼規約掃描”

結果:

5.Maven打包加入PMD校驗

到目前為止,我們已經做到了能在開發階段實時校驗自己的代碼了,最後我們需要把規約檢查加入到代碼打包中,這樣才能做到部署到生產環境的代碼都是符合規範的,如果不符合規範,打包會失敗。

考慮到大多數項目使用maven管理,可以把自定義pmd規則整合到maven,這樣就可以使用maven校驗代碼規則了

在maven項目中加入pmd插件,配置插件在package階段執行。通常我們的項目都有一個公共的父pom,那將插件加入到父pom中就行

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-pmd-plugin</artifactId>
                <version>3.8</version>

                <configuration>
                    <rulesets>
                        <ruleset>rulesets/java/ali-comment.xml</ruleset>
                        <ruleset>rulesets/java/ali-concurrent.xml</ruleset>
                        <ruleset>rulesets/java/ali-constant.xml</ruleset>
                        <ruleset>rulesets/java/ali-exception.xml</ruleset>
                        <ruleset>rulesets/java/ali-flowcontrol.xml</ruleset>
                        <ruleset>rulesets/java/ali-naming.xml</ruleset>
                        <ruleset>rulesets/java/ali-oop.xml</ruleset>
                        <ruleset>rulesets/java/ali-orm.xml</ruleset>
                        <ruleset>rulesets/java/ali-other.xml</ruleset>
                        <ruleset>rulesets/java/ali-set.xml</ruleset>
                    </rulesets>
                    <printFailingErrors>true</printFailingErrors>
                    <!--掃描級別,小於等於這個級別的錯誤代碼將不通過掃描。不配預設是5-->
                    <minimumPriority>1</minimumPriority>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>com.alibaba.p3c</groupId>
                        <artifactId>p3c-pmd</artifactId>
                        <version>1.3.3</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

如果存在不符合規範代碼打包將失敗:

關於maven pmd插件更詳細介紹參考官網

6.總結

本文篇幅確實有點長,看懂需要有點耐心。不過其實也挺簡單,關鍵點就是分析抽象語法樹,找出問題代碼節點,剩下的工作就很簡單了。

PMD也有局限性,比如只能校驗java源文件,對於XML等配置規約就沒轍了。還有最最重要的,代碼邏輯等關鍵性問題是沒法校驗的,也沒法做。PMD只是一定程度上規範了我們的代碼,要寫出優雅的代碼,還得多思考多實踐吶。

來源:blog.csdn.net/u014513883/article/details/79186893

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發佈,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在項目開發過程中,不可避免的會碰到需要強制增加審計日誌的需求,那具體如何做呢,本文將告訴你答案! ...
  • IO多路復用通過某種機制使進程監聽某些文件描述符,當文件描述符中有讀或寫就緒時,進程能夠收到系統內核發送的相應通知從而進行相應的IO操作;IO多路復用有:select、poll、epoll等模式,這裡主要介紹select;select本質上也是同步IO,調用時阻塞自己,IO事件就緒後被喚醒返回負責讀 ...
  • TreeSet:能夠對元素按照某種規則進行排序。 * 排序有兩種方式 * A:自然排序 * B:比較器排序 * * TreeSet集合的特點:排序和唯一 * * 通過觀察TreeSet的add()方法,我們知道最終要看TreeMap的put()方法 A:自然排序 1 public class Tre ...
  • 周末不能出去玩多無聊啊,那就來幾個小游戲給大家助助興,,可以自己復現玩玩,研究下裡面的編程邏輯,對學習編程(特別是初學者)應該會有很大幫助。學會了別忘記教你的小伙伴,好的東西大家要學會分享。 由於文章較長,大家可以先點贊收藏後再慢慢看哦~ 1、吃金幣 源碼分享: #####Python學習Q群:90 ...
  • 枚舉 對於一些簡單的題目,我們或許不需要用什麼太巧妙的方法,只需要把所有的可能性列舉出來,然後逐一試驗就可以了。 總的來說,枚舉就是通過列舉所有的可能性進行一一判斷檢查。 方法 通過事先把各種可能發生的事情都列舉一遍,為後面求解提供結果。 總的來說,兩種方法: 遞歸枚舉,這種方法往往更直觀; 遞推( ...
  • 3 原碼、反碼、補碼 3.1 知識點補充 在電腦內部,所有信息都是用二進位數串的形式表示的。整數通常都有正負之分,電腦中的整數分為無符號的和帶符號的。無符號的整數用來表示0和正整數,即自然數;帶符號的正數可以表示所有的整數。 由於電腦中符號和數字一樣,都必須用二進位數串來表示,因此,正負號也必 ...
  • 阿珍:“老徐,你這茶杯了泡的什麼?紅紅的。” 老徐:“這是枸杞呀。” 阿珍:“枸杞?你最近什麼乾多了,這麼虛!” 老徐:“怎麼可能?看我這身體,不弱的好吧!” 阿珍一臉壞笑地說:“那就是軟了。” 老徐的老臉一紅,辯解到:“我這是養養生,我很強的,好吧。” ...
  • GoSDK安裝 下載 GO SDK 配置環境變數 在文件最後添加以下內容 方式一:系統環境變數 方式二:用戶環境變數 更新配置文件 查看go-SDK 是否安裝成功 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...