質量是衡量一個軟件是否成功的關鍵要素。而對於商業軟件系統,尤爲是企業應用軟件系統來講,除了軟件運行質量、文檔質量之外,代碼的質量也是很是重要的。軟件開發進行到編碼階段的時候,最大的風險就在於如何保證代碼的易讀性和一致性,從而使得軟件的維護的代價不會很高。php
在軟件開發的過程當中,如下幾種情形隨處可見:html
1) 軟件維護時間長,並且維護人員的積極性不高:java
作過軟件維護的開發人員,尤爲是在接手不是本身開發產品的源碼的時候,即便有良好的文檔說明,仍然會對代碼中冗長、沒有註釋的段落「歎爲觀止」。理解尚且如此困難,況且要修改或者增長新的功能。所以,不少開發人員不肯意進行軟件維護的工做。node
2)新的開發人員融入團隊的時間比較長:編程
除了沒有良好的培訓、文檔等有效的機制之外,每一個人一套的編碼風格,也容易形成新成員對於已有代碼的理解不夠,甚至出現誤差。windows
提升代碼的質量,除了要提升邏輯上的控制以及業務流程的理解外,代碼自己也存在提升的空間,例如一些潛在的問題能夠很早的就避免。相似於編碼規範上的內容,若是全靠編碼人員進行自行檢查,那麼無疑須要很大的工做量,若是可使用代碼的靜態檢查工具進行檢查的話,那麼將大大的提升編碼的效率。eclipse
項目組目前代碼檢查的工做基本上都是經過人工的方式,實行起來比較困難,檢查的效果也不是很明顯。PMD正是這樣一種工具,能夠直接使用它自帶的規則(當 然也可使用本身的規則)對Java源程序進行分析找出程序存在的問題,能夠很大程度上的減輕代碼檢查工做的繁瑣,爲項目組從此的維護和開發工做起到指導 的做用。jsp
本文主要介紹瞭如何使用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對象的操做。
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
你能夠從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
PMD能夠做爲插件集成到不少流行的 IDE中,不少的插件中都包含了PMD的jar文件,這個jar文件中包含了規則集。因此雖然一些插件中使用 rulesets/unusedcode.xml來做爲參數引用規則集,可是其實是使用getResourceAsStream()方法來從PMD的 jar文件中加載。
因爲Eclipse是比較流行的開源Java/J2EE開發IDE,因此本文主要介紹如何在Eclipse中使用PMD工具進行代碼的檢查。
安裝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文件。
一、啓動Eclipse IDE,打開工程,選擇 "Windows"->"Preferences"下的PMD項,其中Rules Configuration 項目能夠配置PMD的檢查規則,自定義檢查規則也能夠在此經過Import的方式導入到PMD中
二、配置好後,鼠標右鍵點擊工程中須要檢查的JavaSource,選擇"PMD"->"Check Code With PMD" ,以後PMD就會經過規則檢查你的JavaSource了而且將信息顯示在PMD本身的視圖上
三、示例
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等警告描述
下面是主要的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就會按照你設定好的規則自動執行代碼檢查了。
選擇合適的規則
運行全部的規則集中的規則會產生很是多的衝突,這些衝突中的不少是不重要的。在這麼多的衝突中尋找你關心的部分結果就沒有什麼效率可言了。
因此須要從明顯的規則集,也就是說必需要改的地方開始是比較好的一個選擇,例如只是運行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