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