静态分析工具PMD
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
1. 編寫目的
質(zhì)量是衡量一個(gè)軟件是否成功的關(guān)鍵要素。而對(duì)于商業(yè)軟件系統(tǒng),尤其是企業(yè)應(yīng)用軟件系統(tǒng)來(lái)說(shuō),除了軟件運(yùn)行質(zhì)量、文檔質(zhì)量以外,代碼的質(zhì)量也是非常重要的。軟件開發(fā)進(jìn)行到編碼階段的時(shí)候,最大的風(fēng)險(xiǎn)就在于如何保證代碼的易讀性和一致性,從而使得軟件的維護(hù)的代價(jià)不會(huì)很高。
在軟件開發(fā)的過(guò)程中,以下幾種情形隨處可見:
1) 軟件維護(hù)時(shí)間長(zhǎng),而且維護(hù)人員的積極性不高:
?做過(guò)軟件維護(hù)的開發(fā)人員,尤其是在接手不是自己開發(fā)產(chǎn)品的源碼的時(shí)候,即使有良好的文檔說(shuō)明,仍然會(huì)對(duì)代碼中冗長(zhǎng)、沒有注釋的段落“嘆為觀止”。理解尚且如此困難,何況要修改或者增加新的功能。因此,很多開發(fā)人員不愿意進(jìn)行軟件維護(hù)的工作。
2)新的開發(fā)人員融入團(tuán)隊(duì)的時(shí)間比較長(zhǎng):
除了沒有良好的培訓(xùn)、文檔等有效的機(jī)制以外,每個(gè)人一套的編碼風(fēng)格,也容易造成新成員對(duì)于已有代碼的理解不夠,甚至出現(xiàn)偏差。
?
提高代碼的質(zhì)量,除了要提高邏輯上的控制以及業(yè)務(wù)流程的理解外,代碼本身也存在提高的空間,例如一些潛在的問(wèn)題可以很早的就避免。類似于編碼規(guī)范上的內(nèi)容,如果全靠編碼人員進(jìn)行自行檢查,那么無(wú)疑需要很大的工作量,如果可以使用代碼的靜態(tài)檢查工具進(jìn)行檢查的話,那么將大大的提高編碼的效率。
?
項(xiàng)目組目前代碼檢查的工作基本上都是通過(guò)人工的方式,實(shí)行起來(lái)比較困難,檢查的效果也不是很明顯。PMD正是這樣一種工具,可以直接使用它自帶的規(guī)則(當(dāng) 然也可以使用自己的規(guī)則)對(duì)Java源程序進(jìn)行分析找出程序存在的問(wèn)題,可以很大程度上的減輕代碼檢查工作的繁瑣,為項(xiàng)目組今后的維護(hù)和開發(fā)工作起到指導(dǎo) 的作用。
本文主要介紹了如何使用pmd工具進(jìn)行代碼的自動(dòng)化檢查,以規(guī)避一些潛在的問(wèn)題并找出代碼的邏輯錯(cuò)誤。
2. PMD簡(jiǎn)介
PMD是一種開源分析Java代碼錯(cuò)誤的工具。與其他分析工具不同的是,PMD通過(guò)靜態(tài)分析獲知代碼錯(cuò)誤。也就是說(shuō),在不運(yùn)行Java程序的情況下報(bào)告錯(cuò)誤。PMD附帶了許多可以直接使用的規(guī)則,利用這些規(guī)則可以找出Java源程序的許多問(wèn)題,例如:
??潛在的bug:空的try/catch/finally/switch語(yǔ)句
??未使用的代碼:未使用的局部變量、參數(shù)、私有方法等
? 可選的代碼:String/StringBuffer的濫用
??復(fù)雜的表達(dá)式:不必須的if語(yǔ)句、可以使用while循環(huán)完成的for循環(huán)
??重復(fù)的代碼:拷貝/粘貼代碼意味著拷貝/粘貼bugs
??循環(huán)體創(chuàng)建新對(duì)象:盡量不要再for或while循環(huán)體內(nèi)實(shí)例化一個(gè)新對(duì)象
@ 資源關(guān)閉:Connect,Result,Statement等使用之后確保關(guān)閉掉
此外,用戶還可以自己定義規(guī)則,檢查Java代碼是否符合某些特定的編碼規(guī)范。例如,你可以編寫一個(gè)規(guī)則,要求PMD找出所有創(chuàng)建Thread和Socket對(duì)象的操作。
?
3. 工作原理
PMD的核心是JavaCC解析器生成器。PMD結(jié)合運(yùn)用JavaCC和EBNF(擴(kuò)展巴科斯-諾爾范式,Extended Backus-Naur Formal)語(yǔ)法,再加上JJTree,把Java源代碼解析成抽象語(yǔ)法樹(AST,Abstract Syntax Tree)。顯然,這句話不那么好懂,且看下文具體說(shuō)明。?
從根本上看,Java源代碼只是一些普通的文本。不過(guò),為了讓解析器承認(rèn)這些普通的文本是合法的Java代碼,它們必須符合某種特定的結(jié)構(gòu)要求。這種 結(jié)構(gòu)可以用一種稱為EBNF的句法元語(yǔ)言表示,通常稱為“語(yǔ)法”(Grammar)。JavaCC根據(jù)語(yǔ)法要求生成解析器,這個(gè)解析器就可以用于解析用 Java編程語(yǔ)言編寫的程序。?
不過(guò)實(shí)際運(yùn)行中的PMD還要經(jīng)過(guò)JJTree的一次轉(zhuǎn)換。JJTree是一個(gè)JavaCC的插件,通過(guò)AST擴(kuò)充JavaCC生成的解析器。AST是一個(gè)Java符號(hào)流之上的語(yǔ)義層。有了JJTree,語(yǔ)法分析的結(jié)果不再是“System, ., out, ., . println”之類的符號(hào)序列,而是一個(gè)由對(duì)象構(gòu)成的樹型層次結(jié)構(gòu)。例如,下面是一段簡(jiǎn)單的Java代碼以及與之對(duì)應(yīng)的AST。?
Java源代碼:
public class Foo {
public void bar() {
System.out.println("hello world");
}
}
對(duì)應(yīng)的抽象語(yǔ)法樹
CompilationUnit
TypeDeclaration
ClassDeclaration
UnmodifiedClassDeclaration
ClassBody
ClassBodyDeclaration
MethodDeclaration
ResultType
MethodDeclarator
FormalParameters
Block
BlockStatement
Statement
StatementEXPression
PrimaryExpression
PrimaryPrefix
Name
PrimarySuffix
Arguments
ArgumentList
Expression
PrimaryExpression
PrimaryPrefix
Literal
4. PMD的安裝和運(yùn)行
4.1安裝并從命令行運(yùn)行PMD
你可以從PMD的網(wǎng)站下載PMD的二進(jìn)制版本,或下載帶源代碼的版本,下載得到的都是ZIP文件。假設(shè)你下載了二進(jìn)制版本,先把它解壓縮到任意一個(gè)目錄。 接下來(lái)怎么做,就要看你準(zhǔn)備怎么用它——最簡(jiǎn)單的,如果要在一個(gè)Java源代碼目錄中運(yùn)行PMD,只需直接在命令行上運(yùn)行下面的命令:
?
E:\SoftWare\pmd-bin-4.2.1\pmd-4.2.1\bin>java -jar ..\lib\pmd-4.2.1.jar D:\ebsser
vice\ebsservice\src text rulesets/unusedcode.xml
?
輸出結(jié)果類如:
D:\ebsservice\ebsservice\src\com\sinosoft\service\policy\ebs\SMPolicyInput.java:
51????? Avoid unused private fields such as 'logger'.
D:\ebsservice\ebsservice\src\com\sinosoft\service\policy\ebs\SMPolicyShow.java:2
5?????? Avoid unused private fields such as 'logger'.
D:\ebsservice\ebsservice\src\com\sinosoft\service\policy\ebs\SMQueryPolicyByPoli
cyNo.java:32??? Avoid unused local variables such as 'visaStatus'.
D:\ebsservice\ebsservice\src\com\sinosoft\service\policy\ebs\SMQueryPolicyByPoli
cyNo.java:44??? Avoid unused local variables such as 'temp'.
D:\ebsservice\ebsservice\src\com\sinosoft\service\policy\ebs\erisk\ESMPolicyInpu
t.java:28?????? Avoid unused private fields such as 'logger'.
D:\ebsservice\ebsservice\src\com\sinosoft\service\policy\ebs\jrisk\JSMPolicyInpu
t.java:22?????? Avoid unused private fields such as 'logger'.
?
一些可以加載必須參數(shù)前面或者后面的可選參數(shù)如下:
-debug: 打印debug日志信息
-targetjdk: 指定目標(biāo)源代碼的版本- 1.3, 1.4, 1.5, 1.6 or 1.7;
默認(rèn)是1.5
-cpus: 指定創(chuàng)建的線程數(shù)
-encoding: 指定PMD檢查的代碼的編碼方式
-excludemarker: 指定PMD需要忽略的行的標(biāo)記,默認(rèn)為NOPMD
-shortnames: 在報(bào)告中顯示縮短的文件名
-linkprefix: HTML源文件的路徑,只是為了HTML顯示
-lineprefix: 自定義的錨,用于影響源文件中的行,只是用于HTML顯示
-minimumpriority: 規(guī)則的優(yōu)先級(jí)限制,低于優(yōu)先級(jí)的規(guī)則將不被使用
-nojava: 不檢查java文件,默認(rèn)是檢查java文件
-jsp: 檢查JSP/JSF文件,默認(rèn)不檢查
-reportfile: 將報(bào)告輸出到文件,默認(rèn)是打印在控制臺(tái)
-benchmark: 輸出一個(gè)基準(zhǔn)清單,默認(rèn)輸出到控制臺(tái)
-xslt: 覆蓋默認(rèn)的xslt
-auxclasspath: 指定源代碼文件使用的類路徑
?
例如在windows系統(tǒng)中,例子如下:
c:\> java -jar pmd-4.2.1.jar c:\my\source\code text unusedcode,imports -targetjd
k 1.5 -debug
c:\> java -jar pmd-4.2.1.jar c:\my\source\code xml basic,design -encoding UTF-8
c:\> java -jar pmd-4.2.1.jar c:\my\source\code html typeresolution -auxclasspath
?commons-collections.jar;derby.jar
?
4.2在Eclipse中安裝PMD插件運(yùn)行方式
PMD可以作為插件集成到很多流行的 IDE中,很多的插件中都包含了PMD的jar文件,這個(gè)jar文件中包含了規(guī)則集。所以雖然一些插件中使用 rulesets/unusedcode.xml來(lái)作為參數(shù)引用規(guī)則集,但是實(shí)際上是使用getResourceAsStream()方法來(lái)從PMD的 jar文件中加載。
由于Eclipse是比較流行的開源Java/J2EE開發(fā)IDE,所以本文主要介紹如何在Eclipse中使用PMD工具進(jìn)行代碼的檢查。
?
4.2.1 安裝基于Eclipse IDE的插件
安裝Eclipse的PMD插件的過(guò)程如下:
??啟動(dòng)Eclipse
? 選擇Help-->Software Updates-->Find and Install
??選擇Next,選擇New remote site
??在Name框中輸入PMD,URL框中輸入http://pmd.sf.net/eclipse
??在之后的對(duì)話框中一直點(diǎn)擊下一步或者接受協(xié)議,完成Eclipse的PMD插件的安裝
也可以通過(guò)下載最新的zip文件按,然后執(zhí)行上述過(guò)程,只是使用New locale site來(lái)代替New remote site,并使用下載的zip文件。
可以通過(guò)Windows-->Preferences來(lái)配置PMD。
通過(guò)右鍵一個(gè)項(xiàng)目,然后選擇PMD-->Check node with PMD,即可使用PMD工具檢查代碼。如果要進(jìn)行重復(fù)代碼檢測(cè),那么右鍵一個(gè)項(xiàng)目后,選擇PMD-->Find suspect cut and paste。檢查結(jié)果會(huì)放在reports目錄下,文件名為cpd-report.txt。
可以通過(guò)使用Eclipse的幫助系統(tǒng)來(lái)查看PMD插件的文檔。
在安裝完更新后,如果發(fā)生了一個(gè)異常,例如”java.lang.RuntimeException: Could not find that class xxxx”,這時(shí)試著刪除workspace中的.metadata/plugins/net.sourceforge.pmd.eclipse目錄下的ruleset.xml文件。
?
4.2.2 使用PMD
1、啟動(dòng)Eclipse IDE,打開工程,選擇?"Windows"->"Preferences"下的PMD項(xiàng),其中Rules Configuration 項(xiàng)目可以配置PMD的檢查規(guī)則,自定義檢查規(guī)則也可以在此通過(guò)Import的方式導(dǎo)入到PMD中
2、配置好后,鼠標(biāo)右鍵點(diǎn)擊工程中需要檢查的JavaSource,選擇"PMD"->"Check Code With PMD" ,之后PMD就會(huì)通過(guò)規(guī)則檢查你的JavaSource了并且將信息顯示在PMD自己的視圖上
3、示例
import java.util.*;
public class Test {
??? ????public static void main(String[] args) {
?????????????try{?
????????????????if(true)?{}
????????????????System.out.println("Hello World!");
????????????}?catch(Exception e) {
????????????}?
???????}
}
以上代碼PMD會(huì)檢查出:catch塊中沒有內(nèi)容、if判斷塊中沒有內(nèi)容、代碼中出現(xiàn)System.out.println等警告描述
4.3 使用Ant進(jìn)行調(diào)用
下面是主要的Ant配置信息
<path?id="pmd.path">????
????<fileset?dir="${lib.dir}/pmd-3.8">
????????<include?name="***.java"/>
????????????</fileset>
????????</pmd>
????</target>
<target?name="cpd">????????
????????<cpd?minimumTokenCount="100"?outputFile="d:/cpd.txt">
????????????<fileset?dir="${src.dir}">
????????????????<include?name="**/*.java"/>
????????????</fileset>
????????</cpd>
????</target>
用Ant命令運(yùn)行build.xml,PMD就會(huì)按照你設(shè)定好的規(guī)則自動(dòng)執(zhí)行代碼檢查了。
5. 關(guān)于PMD規(guī)則
選擇合適的規(guī)則
運(yùn)行所有的規(guī)則集中的規(guī)則會(huì)產(chǎn)生非常多的沖突,這些沖突中的很多是不重要的。在這么多的沖突中尋找你關(guān)心的部分結(jié)果就沒有什么效率可言了。
所以需要從明顯的規(guī)則集,也就是說(shuō)必須要改的地方開始是比較好的一個(gè)選擇,例如只是運(yùn)行unusedcode檢查,然后修改沒有使用的局部變量和成員變量。然后運(yùn)行基本的檢查,修改所有的空語(yǔ)句,例如if語(yǔ)句等。最后可以執(zhí)行與設(shè)計(jì)相關(guān)的或者存在一定爭(zhēng)議的規(guī)則集,或者自定義的規(guī)則集。
自帶規(guī)則的介紹: (PMD插件分析代碼規(guī)則(中文).xls)
PMD 自帶了很多規(guī)則集合,并且分類寫入不同的 ruleset 文件,如
Basic 包含每人都必須遵守的代碼最佳實(shí)踐,如EmptyCatchBlock
Braces 關(guān)于條件分支的規(guī)則,如IfStmtsMustUseBraces
Code Size 關(guān)于代碼大小的規(guī)則,如方法的長(zhǎng)度,參數(shù)的長(zhǎng)度,屬性的個(gè)數(shù)等
Clone 克隆實(shí)現(xiàn)的規(guī)則,如是否有super.clone()
Controversial 一些有爭(zhēng)議的規(guī)則,如UnnecessaryConstructor不必要的構(gòu)造器
Coupling 對(duì)象連接有關(guān)的規(guī)則
Design 可以檢查有問(wèn)題的設(shè)計(jì),如SwitchStmtsShouldHaveDefault
Finalizers 使用finalizers時(shí)需遵循的規(guī)則,如FinalizeOnlyCallsSuperFinalize
Import Statements 和import有關(guān)的規(guī)則,如DuplicateImports重復(fù)import
J2EE 唯一規(guī)則UseProperClassLoader,class.getClassLoader()可能不正確,用
Thread.currentThread().getContextClassLoader() 代替
Javabeans 和javabean規(guī)范有關(guān)的規(guī)則,有BeanMembersShouldSerialize屬性必須
序列化和MissingSerialVersionUID缺少序列化ID
JUnit Tests 和JUnit測(cè)試有關(guān)的,如JUnitSpelling拼寫檢查等
Logging (Java) 檢查L(zhǎng)ogger的一些錯(cuò)誤用法,如MoreThanOneLogger多個(gè)Logger
Logging (Jakarta) 使用Jakarta Logger的一些規(guī)則,有UseCorrectExceptionLogging
異常處理不當(dāng)和ProperLogger是否正確定義Logger
Migrating JDK 版本移植的規(guī)則,如ReplaceVectorWithList用List代替Vector
Naming 和命名有關(guān)的規(guī)則,名稱太短或太長(zhǎng),命名的約定等EN-U
轉(zhuǎn)載于:https://my.oschina.net/u/273709/blog/78945
總結(jié)
- 上一篇: 化了妆的祝福 4
- 下一篇: debian6 xen4.0安装 gue