靜態分析工具PMD使用說明

http://blog.csdn.net/sadamdiyi/article/details/6073694


質量是衡量一個軟件是否成功的關鍵要素。而對於商業軟件系統,尤其是企業應用軟件系統來說,除了軟件運行質量、文檔質量以外,代碼的質量也是非常重要的。軟件開發進行到編碼階段的時候,最大的風險就在於如何保證代碼的易讀性和一致性,從而使得軟件的維護的代價不會很高。

在軟件開發的過程中,以下幾種情形隨處可見:

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="**/*.jar" />

    </fileset>

</path>

<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpathref="pmd.path"/>

<taskdef name="cpd" classname="net.sourceforge.pmd.cpd.CPDTask" classpathref="pmd.path"/>

    <target name="pmd">

        <pmd shortFilenames="true">

            <ruleset>rulesets/favorites.xml</ruleset>            

            <formatter type="html" toFile="d:/foo.html" toConsole="false"/>

            <fileset dir="${src.dir}">

                <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 和命名有關的規則,名稱太短或太長,命名的約定等


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章