靜態分析工具PMD

1. 編寫目的

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

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

1) 軟件維護時間長,並且維護人員的積極性不高:java

 作過軟件維護的開發人員,尤爲是在接手不是本身開發產品的源碼的時候,即便有良好的文檔說明,仍然會對代碼中冗長、沒有註釋的段落「歎爲觀止」。理解尚且如此困難,況且要修改或者增長新的功能。所以,不少開發人員不肯意進行軟件維護的工做。node

2)新的開發人員融入團隊的時間比較長:編程

 除了沒有良好的培訓、文檔等有效的機制之外,每一個人一套的編碼風格,也容易形成新成員對於已有代碼的理解不夠,甚至出現誤差。windows

 

提升代碼的質量,除了要提升邏輯上的控制以及業務流程的理解外,代碼自己也存在提升的空間,例如一些潛在的問題能夠很早的就避免。相似於編碼規範上的內容,若是全靠編碼人員進行自行檢查,那麼無疑須要很大的工做量,若是可使用代碼的靜態檢查工具進行檢查的話,那麼將大大的提升編碼的效率。eclipse

 

項目組目前代碼檢查的工做基本上都是經過人工的方式,實行起來比較困難,檢查的效果也不是很明顯。PMD正是這樣一種工具,能夠直接使用它自帶的規則(當 然也可使用本身的規則)對Java源程序進行分析找出程序存在的問題,能夠很大程度上的減輕代碼檢查工做的繁瑣,爲項目組從此的維護和開發工做起到指導 的做用。jsp

本文主要介紹瞭如何使用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

一、啓動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等警告描述

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

相關文章
相關標籤/搜索