裝上這個插件,讓自己的代碼更規(guī)范!
點擊“藍字”,關(guān)注,置頂公眾號
每日技術(shù)干貨,第一時間送達!
?
1
前言
工作中難免會遇到維護別人代碼的情況,那么首先就得看懂別人寫的代碼。如果對方寫的代碼混亂臃腫,維護成本必然很高,如果對方寫的代碼優(yōu)雅清晰,那維護的人看起來必然心情愉悅。正所謂“前人栽樹,后人乘涼;前人埋坑,后人罵娘”。
代碼首先是給人看的,其次才是給機器看到,如何編寫出任何人都看到懂的代碼?答案是制定規(guī)范!
每個公司都會有自己的編碼規(guī)范,但是往往的情況是趕項目進度或者懶惰或者個人水平習(xí)慣等原因,加上沒有code review,最后代碼就寫的千奇百怪了。原因就在于規(guī)范是有了,但是沒人遵守。所以,編碼規(guī)范需要強制執(zhí)行,交給工具來強制執(zhí)行。
本文將通過介紹java靜態(tài)代碼檢查工具PMD、阿里巴巴p3c開源項目到最后編寫自定義編碼規(guī)約來學(xué)習(xí)如何規(guī)范代碼的編寫。
?
2
PMD靜態(tài)代碼掃描
2.1.PMD官網(wǎng)
https://pmd.github.io/
2.2.概述
PMD是一種開源分析Java代碼錯誤的工具。它通過靜態(tài)分析獲知代碼錯誤。也就是說,在不運行Java程序的情況下報告錯誤。PMD附帶了許多可以直接使用的規(guī)則,利用這些規(guī)則可以找出Java源程序的許多問題,例如:
潛在的bug:空的try/catch/finally/switch語句
未使用的代碼:未使用的局部變量、參數(shù)、私有方法等
可選的代碼:String/StringBuffer的濫用
復(fù)雜的表達式:不必須的if語句、可以使用while循環(huán)完成的for循環(huán)
重復(fù)的代碼:拷貝/粘貼代碼意味著拷貝/粘貼bugs
循環(huán)體創(chuàng)建新對象:盡量不要再for或while循環(huán)體內(nèi)實例化一個新對象
資源關(guān)閉:Connect,Result,Statement等使用之后確保關(guān)閉掉
此外,用戶還可以自己定義規(guī)則,檢查Java代碼是否符合某些特定的編碼規(guī)范。例如,你可以編寫一個規(guī)則,要求PMD找出所有創(chuàng)建Thread和Socket對象的操作。
2.3.工作原理
PMD的核心是JavaCC解析器生成器。PMD結(jié)合運用JavaCC和EBNF(擴展巴科斯-諾爾范式,Extended Backus-Naur Formal)語法,再加上JJTree,把Java源代碼解析成抽象語法樹(AST,Abstract Syntax Tree)
從根本上看,Java源代碼只是一些普通的文本。不過,為了讓解析器承認 這些普通的文本是合法的Java代碼,它們必須符合某種特定的結(jié)構(gòu)要求。這種結(jié)構(gòu)可以用一種稱為EBNF的句法元語言表示,通常稱為“語法” (Grammar)。JavaCC根據(jù)語法要求生成解析器,這個解析器就可以用于解析用Java編程語言編寫的程序。
2.4.規(guī)則分類
最佳實踐:公認的最佳實踐的規(guī)則。
代碼風(fēng)格:這些規(guī)則強制執(zhí)行特定的編碼風(fēng)格。
設(shè)計:幫助您發(fā)現(xiàn)設(shè)計問題的規(guī)則。
文檔:這些規(guī)則與代碼文檔有關(guān)。
容易出錯的規(guī)則:用于檢測被破壞的、非常混亂的或容易發(fā)生運行時錯誤的結(jié)構(gòu)的規(guī)則。
多線程:這些規(guī)則在處理多個執(zhí)行線程時標(biāo)記問題。
性能:標(biāo)記存在性能問題的代碼的規(guī)則。
安全:顯示潛在安全缺陷的規(guī)則。
2.5.編寫PMD自定義規(guī)則
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開發(fā)規(guī)約插件p3c
3.1.GITHUB地址
https://github.com/alibaba/p3c
3.2.概述
阿里巴巴p3c項目包含三個部分:
p3c-pmd,提供大部分規(guī)則實現(xiàn),基于PMD框架開發(fā),如果想實現(xiàn)自己的規(guī)則,可以基于該模塊開發(fā)(該模塊基于maven編譯打包)
IntelliJ IDEA插件,即idea-plugin模塊(該模塊基于gradle編譯打包)
Eclipse插件,即eclipse-plugin,本文不介紹
3.3.阿里編碼規(guī)約IDEA插件使用
傳送門:https://github.com/alibaba/p3c/wiki
?
4
基于p3c編寫自定義編碼規(guī)則
4.1.自定義規(guī)則
假設(shè)現(xiàn)在需要開發(fā)這么一個規(guī)則:方法請求參數(shù)列表不允許超過(含)5個
4.2.開發(fā)步驟
4.2.1.找出問題代碼,使用pmd圖形化工具解析成抽象語法樹
代碼示例:
public class Demo {public void methodA(int a) {}public void methodB(int a, int b, int c, int d, int e) {}}
將源碼放入Source Code框,點擊Go按鈕,解析結(jié)果顯示在左下框

4.2.2.分析抽象語法樹
可以看到,整棵樹的根節(jié)點是CompilationUnit,即編譯單元,代表每個java源文件。我們首先要找到所有的方法聲明,根據(jù)樹節(jié)點名稱大概也能看出來是MethodDeclaration,點擊相應(yīng)的節(jié)點,看看光標(biāo)是否定位到源碼方法聲明位置。
仔細分析MethodDeclaration節(jié)點,可以看到他有以下幾個直接子節(jié)點:ResultType、MethodDeclarator、Block,即返回類型、方法聲明、方法體

MethodDeclarator是我們想找的節(jié)點XPATH表達式可以這么寫:
//CompilationUnit//MethodDeclarator驗證表達式是否正確,將它寫到PMD圖形界面XPATH Query框中,點擊Go按鈕

接下來,我們需要找到每個方法對應(yīng)的參數(shù)列表,參數(shù)列表節(jié)點是方法節(jié)點的直接子節(jié)點,完整XPATH表達式為:
//CompilationUnit//MethodDeclarator/FormalParameters
獲取到參數(shù)列表節(jié)點后,我們查看該節(jié)點的屬性,找出參數(shù)個數(shù)的屬性,觀察可以發(fā)現(xiàn)是ParameterCount屬性。
到現(xiàn)在為止,抽象語法樹已經(jīng)分析完,我們知道這么找出代碼中參數(shù)列表大于等于5個的方法了。
4.2.3.p3c-pmd項目編寫自定義代碼規(guī)則
打開阿里p3c-pmd工程,開始編寫我們的自定義規(guī)則。
阿里已經(jīng)寫了很多規(guī)則,我們現(xiàn)在要編寫的規(guī)則屬于面向?qū)ο蠓懂牐梢园岩?guī)則寫到opp包下,新建一個規(guī)則類MethodParameterCountRule,繼承自AbstractAliRule,重寫 visit方法:

/**?*?方法參數(shù)列表個數(shù)不宜過長?*?*?@auther?qingjian.wu?*?@create?2018-01-27?14:59?*/public?class?MethodParameterCountRule?extends?AbstractAliRule{????private?static?final?String?METHOD_XPATH?=?"http://MethodDeclarator";private static final Integer PARAMETER_COUNT_LIMIT = 5;????@Override????public?Object?visit(ASTCompilationUnit?node,?Object?data)?{????????try?{????????????//?找到所方法節(jié)點????????????List?methodNodes?=?node.findChildNodesWithXPath(METHOD_XPATH); ????????????if?(methodNodes?!=?null?&&?methodNodes.size()?>?0)?{????????????????for?(Node?methodNode?:?methodNodes)?{????????????????????//?找到每個方法的參數(shù)列表聲明????????????????????List?formalParameters?=?methodNode.findChildrenOfType(ASTFormalParameters.class); ????????????????????if?(formalParameters.get(0).getParameterCount()?>=?PARAMETER_COUNT_LIMIT)?{????????????????????????//?違反規(guī)則提示信息,第二個參數(shù)是提示信息位置,第三個參數(shù)是提示信息key,第四個參數(shù)用來替換提示信息????????????????????????//?中的占位符,這里獲取的節(jié)點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項目配置規(guī)則
將編寫好規(guī)則配置到ali-oop.xml文件中

????"MethodParameterCountRule" ??????????language="java"??????????message="java.oop.MethodParameterCountRule.rule.msg"??????????class="com.alibaba.p3c.pmd.lang.java.rule.oop.MethodParameterCountRule">????????????????<priority>1priority>????????<example>????????????Negative?example:????public?void?methodB(int?a,?int?b,?int?c,?int?d,?int?e)?{????}]]>????????example>????????<example>????????????Positive?example:public void methodA() {????}]]>????????example>rule>
4.2.5.p3c-pmd項目編寫提示信息
上兩步使用的提示信息和規(guī)則信息需要編寫到message.xml配置文件中,message_en.xml中是英文提示,這里就先不演示了

<entry key="java.oop.MethodParameterCountRule.violation.msg">entry><entry key="java.oop.MethodParameterCountRule.rule.msg">entry>
4.2.6.單元測試
編寫測試樣例,將要測試的源代碼寫到test目錄對應(yīng)的xml文件中

<test-data><code-fragment id="測試樣例">??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>0expected-problems><code-ref id="測試樣例" />test-code>test-data>
編寫單元測試

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

4.3.配置插件
4.3.1.p3c-pmd打包安裝到本地maven倉庫
先把用不到的插件maven-javadoc-plugin和maven-gpg-plugin注釋掉,然后運行mvn命令:
mvn -DskipTests=true clean install4.3.2.idea-plugin項目打包插件
idea-plugin項目基于gradle構(gòu)建,配置根目錄下build.gradle,讓構(gòu)建使用本地私有maven倉庫構(gòu)建

然后運行開始gradle構(gòu)建:
clean buildDependents build打包成功后會在idea-plugin\p3c-idea\build\distributions\目錄下生成Alibaba Java Coding Guidelines-1.0.0.zip文件,這個就是我們加入了自己拓展阿里開發(fā)規(guī)約的插件,IDEA中安裝此插件
Settings->Plugins->Install plugin from disk

4.3.3.IDEA中使用編碼規(guī)約插件
安裝完插件重啟IDEA,用之前的代碼測試下插件是否生效。
右鍵點擊“編碼規(guī)約掃描”

結(jié)果:

?
5
Maven打包加入PMD校驗
到目前為止,我們已經(jīng)做到了能在開發(fā)階段實時校驗自己的代碼了,最后我們需要把規(guī)約檢查加入到代碼打包中,這樣才能做到部署到生產(chǎn)環(huán)境的代碼都是符合規(guī)范的,如果不符合規(guī)范,打包會失敗。
考慮到大多數(shù)項目使用maven管理,可以把自定義pmd規(guī)則整合到maven,這樣就可以使用maven校驗代碼規(guī)則了
在maven項目中加入pmd插件,配置插件在package階段執(zhí)行。通常我們的項目都有一個公共的父pom,那將插件加入到父pom中就行
<build><plugins><plugin><groupId>org.apache.maven.pluginsgroupId><artifactId>maven-pmd-pluginartifactId><version>3.8version><configuration><rulesets>????????????????????????<ruleset>rulesets/java/alicomment.xmlruleset>????????????????????????<ruleset>rulesets/java/aliconcurrent.xmlruleset>????????????????????????<ruleset>rulesets/java/aliconstant.xmlruleset>????????????????????????<ruleset>rulesets/java/aliexception.xmlruleset>????????????????????????<ruleset>rulesets/java/aliflowcontrol.xmlruleset>????????????????????????<ruleset>rulesets/java/alinaming.xmlruleset>????????????????????????<ruleset>rulesets/java/alioop.xmlruleset>????????????????????????<ruleset>rulesets/java/aliorm.xmlruleset>????????????????????????<ruleset>rulesets/java/aliother.xmlruleset>????????????????????????<ruleset>rulesets/java/aliset.xmlruleset>rulesets><printFailingErrors>trueprintFailingErrors><minimumPriority>1minimumPriority>configuration><executions><execution><phase>packagephase><goals><goal>checkgoal>goals>execution>executions><dependencies><dependency><groupId>com.alibaba.p3cgroupId><artifactId>p3c-pmdartifactId><version>1.3.3version>dependency>dependencies>plugin>plugins>build>
如果存在不符合規(guī)范代碼打包將失敗:

關(guān)于maven pmd插件更詳細介紹參考官網(wǎng)
?
6
總結(jié)
?
本文篇幅確實有點長,看懂需要有點耐心。不過其實也挺簡單,關(guān)鍵點就是分析抽象語法樹,找出問題代碼節(jié)點,剩下的工作就很簡單了。
PMD也有局限性,比如只能校驗java源文件,對于XML等配置規(guī)約就沒轍了。還有最最重要的,代碼邏輯等關(guān)鍵性問題是沒法校驗的,也沒法做。PMD只是一定程度上規(guī)范了我們的代碼,要寫出優(yōu)雅的代碼,還得多思考多實踐吶。
來源:blog.csdn.net/u014513883/article/details/79186893
往期推薦
