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自定義規則
- https://pmd.github.io/pmd-5.4.1/customizing/howtowritearule.html
- https://testerhome.com/topics/4918
- http://www.w3school.com.cn/xpath/index.asp
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"
>
<!--級別,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開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/449745.html
標籤:Java
下一篇:九、Java例外機制
