2.安裝Eclipse的FindBugs插件
可以在下面的地址
http://findbugs.sourceforge.net/downloads.html
打開頁面內容如下:
下載FindBugs軟件以及eclipse和blueJ的插件。
- FindBugs tool (standard version with command line, ant, and Swing interfaces)
- findbugs-1.3.9.tar.gz
- findbugs-1.3.9.zip ----WINDOWS下載FindBugs軟件,可獨立運行.並可在Ant中使用FindBugs
- findbugs-1.3.9-source.zip
- Eclipse plugin for FindBugs version 1.3.9.20090821 (requires Eclipse 3.3 or later)
- edu.umd.cs.findbugs.plugin.eclipse_1.3.9.20090821.zip --------eclipse 的FindBugs插件,不能獨立運行,只能集成到eclipse插件運行
- eclipsePlugin-1.3.9.20090821-source.zip
The Eclipse plugin may also be obtained from one of the FindBugs Eclipse plugin update sites: --FindBugs插件的更新地址有很多,根據類型的不同,包括以下幾個:
- http://findbugs.cs.umd.edu/eclipse update site for official releases ---只提供FindBugs的官方釋放版本。
- http://findbugs.cs.umd.edu/eclipse-candidate update site for candidate releases and official releases --提供FindBugs的官方釋放版本和可選釋放版本。
- http://findbugs.cs.umd.edu/eclipse-daily update site for all releases, including developmental ones --提供最新的FindBugs的插件。除了編譯通過外並沒有進行測試的版本。
單擊上面鏈接即可下載(另存).
也可以在下面的地址:
http://prdownloads.sourceforge.net/findbugs
下載插件的zip文件,將其解壓縮到eclipse的plugin目錄(<eclipse_install_dir>/plugins)。
安裝完插件後,可以使用Help-->About Eclipse Platform-->Plug-in Details來查看FindBugs插件的使用方法。
3. 在Eclipse中使用FindBugs插件
運行FindBugs插件的方法很簡單,選中一個Java工程後,點擊右鍵,選擇Find Bugs,這時就會啓動FindBugs,並且會在有問題的源代碼中顯示標記。
可以自定義FindBugs的運行方式:查看Java工程的屬性對話框,選擇FindBugs的屬性頁,可以看到如下的選項:
→ 啓用/禁用”自動運行FindBugs”複選框---是否在每次修改時進行FindBugs的檢查
→ 選擇最小的警告優先級,並啓用bug的分類---這些選項用於決定顯示哪些問題,例如,如果選擇Medium警告優先級的話,只有Medium和Hign優先級的警告纔會被顯示,類似的,如果不選中Style複選框的話,那麼有關Style類別的警告也不會被顯示。
→ 選擇檢查引擎:對指定的工程啓用那些detectors。
具體的設置畫面如下:
(設置detectors和是否自動運行FindBugs)
(設置啓用的分類)
常見的類型如下:
· 正確性(Correctness):這種歸類下的問題在某種情況下會導致bug,比如錯誤的強制類型轉換等。
· 最佳實踐反例(Bad practice):這種類別下的代碼違反了公認的最佳實踐標準,比如某個類實現了equals方法但未實現hashCode方法等。
· 多線程正確性(Multithreaded correctness):關注於同步和多線程問題。
· 性能(Performance):潛在的性能問題。
· 安全(Security):安全相關。
· 高危(Dodgy):FindBugs團隊認爲該類型下的問題代碼導致bug的可能性很高。
在Eclipse中安裝FindBugs插件
下載Eclipse plugin 的版本,解壓zip文件。
將解壓後的文件放到Eclipse的Plugin中。
重新啓動Eclipse 。
我使用的是MyEclipse8.5可能路徑和大家的不太一樣,我是放到了路徑Genuitec/MyEclipse 8.5/dropins下面
在Eclipse中使用FindBugs
重新啓動eclipse
打開FindBugs視圖
執行Find Bug 任務
右鍵單擊你要檢測的工程、包或文件,-->Find Bugs-->Find Bugs。
check完成後將在Bug Explorer視圖中看到問題列表,該列表以問題類型組織。
展開列表,雙擊列表中具體的問題就可以定位的具體的代碼行。
配置FindBugs
在這裏可以對FindBugs規則等進行詳細設置。
選擇你的項目,右鍵 => Properties => FindBugs =>
1 Run Automatically開關
設置Eclipse自動編譯開關-----即主窗口菜單Project---Build Automatically這個選項勾上就行了.
當此項選中後,FindBugs將會在你修改Java類時自動運行,如你設置了Eclipse自動編譯開關後,當你修改完Java文件保存,FindBugs就會運行,並將相應的信息顯示出來。
當此項沒有選中,你只能每次在需要的時候自己去運行FindBugs來檢查你的代碼。
2 Detector Configuration選擇項
在這裏你可以選擇所要進行檢查的相關的Bug Pattern條目,你可以根據需要選擇或去掉相應的 檢查條件。
3 Minimum priority to report選擇項
這個選擇項是讓你選擇哪個級別的信息進行顯示,有Low、Medium、High三個選擇項可以選擇,很類似於Log4J的級別設置啦。 比如:
你選擇了High選擇項,那麼只有是High級別的提示信息纔會被顯示。
你選擇了Medium選擇項,那麼只有是Medium和High級別的提示信息纔會被顯示。
你選擇了Low選擇項,那麼所有級別的提示信息都會被顯示。
4 Report bug categories選擇項
在這裏是一些顯示Bug分類的選擇:
Malicious code vulnerability關於惡意破壞代碼相關方面的
Correctness關於代碼正確性相關方面的
Internationalization關於代碼國際化相關方面的
Performance關於代碼性能相關方面的
Multithreaded correctness關於代碼多線程正確性相關方面的
另外FindBugs有UI頁面,可以單獨運行。也可以通過Ant以及命令行方式運行。
4. 在Ant中使用FindBugs
Ant作爲一個優秀的自動化構建軟件,大量的應用在Java軟件開發中(雖然有被Maven取代的危險)。FindBugs提供了集成在Ant中使用的Ant Task,可以在自動構建與部署的時候運行FindBugs。
將$FINDBUGS_HOME/lib/findbugs-ant.jar拷貝到$ANT_HOME/lib目錄下以後,就完成了FindBugs的Ant Task的安裝。(強烈建議使用FindBugs附帶的jar文件)
爲了將FindBugs任務集成到Ant腳本中,需要首先進行一個task的定義,如下面的片段所示:---下面ANT的XML內容介紹
<taskdef name=”findbugs” classname=”edu.umd.cs.findbugs.anttask.FindBugsTask” />
在定義了findbugs task之後,就可以使用了。下面是一個例子:
<property name="findbugs.home" value="/export/home/daveho/work/findbugs" />
<target name="findbugs" depends="jar">
<findbugs home="${findbugs.home}"
output="xml"
outputFile="bcel-fb.xml" >
<auxClasspath path="${basedir}/lib/Regex.jar" />
<sourcePath path="${basedir}/src/java" />
<class location="${basedir}/bin/bcel.jar" />
</findbugs>
</target>
findbugs元素必須有home屬性,用於指定FindBugs的安裝路徑。
這是就會在bcel.jar上執行FindBugs。FindBugs的運行結果會被以xml的格式保存在bcel-fb.xml文件中。一個輔助的jar文件被添加到auxClasspath元素中,因爲BCEL庫引用了它。
另外一個例子:
從http://findbugs.sourceforge.net/downloads.html下載最新版本的Findbugs,目前的版本是1.3.0, 於2007年11月8日發佈。把解壓後目錄複製到項目的lib目錄下,然後就可以和Ant配合使用了。FindBugs工作在j2se1.4.0或以後的版本中,需要至少256MB內存。
在Ant腳本中,首先定義Findbugs的解壓目錄位置:
<path id="findbugs.path" >
<fileset dir ="${lib.home}/findbugs-1.3.0">
<include name ="**/*.jar"/>
</fileset>
</path>
接着聲明Findbugs任務:
<taskdef name="findbugs"
classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
classpathref ="findbugs.path"/>
然後建立Findbugs任務:
<property name ="findbugs.home" value ="${lib.home}/findbugs-1.3.0"/>
<!-- 定義findbugs的home,findbugs的task要使用 -->
<target name ="findbugs">
<findbugs home ="${findbugs.home}" includeFilter="${findbugs_include_filter}"
excludeFilter="${findbugs_exclude_filter}"
jvmargs="-Xmx384m" output ="html"
outputFile ="d:/test.html">
<class location ="${build.home}/WEB-INF/classes/"/>
<!-- 以上定義findbugs查找的類路徑 -->
<auxClasspath path="${lib.home}/findbugs-1.3.0/lib/findbugs-ant.jar"/>
<auxClasspath>
<fileset dir="${build.home}/WEB-INF/lib" includes="**/*.jar" />
</auxClasspath>
<!-- 以上定義上述類所依賴的類路徑 -->
<sourcePath path ="${src.home}"/>
<!-- 以上定義源代碼的路徑 -->
</findbugs >
</target >
最後運行ant findbugs即可。
使用過濾器
使用過濾器我們就可以定義使用哪些bug檢測器和針對哪些類進行檢查,因爲一旦項目比較龐大,那查看冗長的bug報告也是十分痛苦的事情。使用過濾器,過濾器用來包含或排除特殊的bug報告。這樣做有助於在特定的時間段內,聚焦我們的關注點。過濾器實際是在一個xml文件定義的,xml配置文件的內容如下:
<FindBugsFilter>
<!-- 所有類使用bugcode爲HE的檢測器 -->
<Match>
<BugCode name ="HE"/>
</Match>
<!-- 該類使用所有的bug檢測器 -->
<Match class ="com.foobar.AClass"/>
<!-- 該類使用bugcode爲HE的檢測器 -->
<Match class ="com.foobar.BClass">
<BugCode name ="HE"/>
</Match>
<!-- 該類的AMethod和BMethod方法使用bugcode爲HE的檢測器 -->
<Match class ="com.foobar.CClass">
<Or>
<Method name ="AMethod"/>
<Method name ="BMethod"/>
</Or>
<BugCode name ="HE"/>
</Match>
</FindBugsFilter>
Findbugs過濾器的一些元素講解:
<FindBugsFilter>
<!-- 該類使用所有的bug檢測器 -->
<Match>
<Class name="com.foobar.MyClass" />
</Match>
<!-- 該類使用bugcode爲HE的檢測器 -->
<Match class ="com.foobar.BClass">
<BugCode name ="HE"/>
</Match>
<!-- 該類通過指定縮寫名使用一些bug檢測器 -->
<Match>
<Class name="com.foobar.MyClass"/ >
<Bug code="DE,UrF,SIC" />
</Match>
<!-- 所有類使用bugcode爲HE的檢測器 -->
<Match>
<BugCode name ="HE"/>
</Match>
<!-- 所有類使用bugcode爲DE,UrF,SIC的檢測器 -->
<Match>
<Bug code="DE,UrF,SIC" />
</Match>
<!-- 所有類通過指定檢測器種類使用某些檢測器 -->
<Match>
<Bug category="PERFORMANCE" />
</Match>
<!-- 該類的指定方法使用bugcode爲DC的檢測器 -->
<Match>
<Class name="com.foobar.MyClass" />
<Or>
<Method name="frob" params="int,java.lang.String" returns="void" />
<Method name="blat" params="" returns="boolean" />
</Or>
<Bug code="DC" />
</Match>
<!-- 該類的AMethod和BMethod方法使用bugcode爲DE,UrF,SIC的檢測器 -->
<Match>
<Class name="com.foobar.MyClass" />
<Or>
<Method name ="AMethod"/>
<Method name ="BMethod"/>
</Or>
<BugCode name ="DE,UrF,SIC "/>
</Match>
<!—該類的指定方法使用bug模式爲OS_OPEN_STREAM的檢測器 -->
<Match>
<Class name="com.foobar.MyClass" />
<Method name="writeDataToFile" />
<Bug pattern="OS_OPEN_STREAM" />
</Match>
<!—該類的某個方法使用優先級爲2的bug模式DLS_DEAD_LOCAL_STORE 的檢測器-->
<Match>
<Class name="com.foobar.MyClass" />
<Method name="someMethod" />
<Bug pattern="DLS_DEAD_LOCAL_STORE" />
<Priority value="2" />
</Match>
<!—代碼的指定部分使用指定bugcode或bug模式的檢測器 -->
<!—所有包的信息類使用bugcode爲UUF的檢測器-->
<Match>
<Class name="~.*/.Messages" />
<Bug code="UUF" />
</Match>
<!—所有內部包使用bugcode爲MS的檢測器-->
<Match>
<Package name="~.*/.internal" />
<Bug code="MS" />
</Match>
<!—ui包層使用bug模式爲SIC_INNER_SHOULD_BE_STATIC_ANON的檢測器-->
<Match>
<Package name="~com/.foobar/.fooproject/.ui.*" />
<Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON" />
</Match>
<!—帶指定標誌的成員域或方法使用指定bugcode或bug模式的檢測器-->
<!—所有類中的void main(String[])方法使用bug模式爲DM_EXIT的檢測器-->
<Match>
<Method returns="void" name="main" params="java.lang.String[]" />
<Bug pattern="DM_EXIT" />
</Match>
<!—所有類中的com.foobar.DebugInfo型的域使用bugcode爲UuF的檢測器-->
<Match>
<Field type="com.foobar.DebugInfo" />
<Bug code="UuF" />
</Match>
</FindBugsFilter>
關於findbugs任務的詳細說明,如下:
class
嵌套元素指定要分析的類。這個元素必須指定一個location屬性,location屬性的名字爲archive文件(jar,zip等)、目錄或者class文件。可以爲一個findbugs元素指定多個class元素。
auxClasspath
可選的嵌套元素,用於指定要分析的類所引用的類,但是並不對引用的類進行分析。
sourcePath
可選的嵌套元素,指定Java源代碼的目錄。
home
必須的屬性,findbugs的安裝目錄。
quietErrors
可選的布爾型屬性。如果是true的話,報告嚴重的分析錯誤和丟失的類。默認情況下爲false。
reportLevel
可選的屬性。指定優先級別。如果是low的話,那麼報告所有的bug,如果是medium(缺省值),報告medium和high優先級的bug。
output
可選屬性,設置輸出格式。
stylesheet
可選屬性,指定生成html時使用的樣式表。
sort
可選屬性,如果輸出屬性設置爲text,該屬性指定是否對輸出結果根據class進行排序,默認爲true。
outputFile
可選屬性,指定輸出文件。
debug
可選的布爾型屬性,是否打印分析過程中的日誌。默認值爲false。
effort
設置分析工作的等級,可以爲min、default和max。
conserveSpace
和min effort一樣的功能。
workHard
和max effort一樣的功能。
visitors
可選屬性,指定逗號分隔的列表,指定要運行的detectors。
omitVisitors
可選屬性,忽略detectors。摺合visitors屬性類似,只是不指定不運行哪些detectors。
excludeFilter
可選屬性,指定排除的Filter。
includeFilter
可選屬性,指定包含的Filter。
projectFile
可選屬性,指定項目的名稱。
jvmargs
可選屬性,指定JVM變量。
systemProperty
系統屬性。
timeout
可選屬性,指定超市的時間,默認爲600,000毫秒,即10分鐘。
failOnError
可選屬性,指定是否在運行FindBugs出現異常時停止構建過程,默認爲false。
errorProperty
可選屬性,如果在運行FindBugs時發生錯誤,指定屬性的值爲true。
warningsProperty
可選屬性,如果在運行FindBugs時發生警告,指定屬性的值爲true。
build.xml實例
Findbugs官方提供了Ant的findbugs操作方法,我們可以通過這樣一個build.xml文件來使用findbugs。
<project name="項目名" default="all">
<property name="findbugs.home" value="findbugs解壓路徑" />
<path id="findbugs.path">
<fileset dir="findbugs解壓路徑">
<include name="**/*.jar" />
</fileset>
</path>
<taskdef name="findbugs"
classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
classpathref="findbugs.path" />
<!-- 定義findbugs的home,findbugs的task要使用 -->
<target name="findbugs">
<findbugs home="${findbugs.home}"
output="xml:withMessages" outputFile="生成的文件">
<!-- 以上定義findbugs查找的類路徑 -->
<auxClasspath path="${findbugs.home}/lib/findbugs-ant.jar" />
<auxClasspath>
<fileset dir="lib"
includes="*.jar" />
</auxClasspath>
<sourcePath path="源文件路徑" />
<class location="生成類路徑" />
</findbugs>
</target>
</project>
比如:我這裏有一個我放在博客上的項目的findbugs的ant操作的build文件。
<project name="Calendar" default="all">
<property name="findbugs.home" value="../../findbugs-1.3.8" />
<path id="findbugs.path">
<fileset dir="../../findbugs-1.3.8">
<include name="**/*.jar" />
</fileset>
</path>
<taskdef name="findbugs"
classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
classpathref="findbugs.path" />
<!-- 定義findbugs的home,findbugs的task要使用 -->
<target name="findbugs">
<mkdir dir="target/findbugs"/>
<findbugs home="${findbugs.home}"
output="xml:withMessages" outputFile="target/findbugs/calendar-fb.xml">
<!-- 以上定義findbugs查找的類路徑 -->
<auxClasspath path="${findbugs.home}/lib/findbugs-ant.jar" />
<auxClasspath>
<fileset dir="lib"
includes="*.jar" />
</auxClasspath>
<sourcePath path="src" />
<class location="target/classes" />
</findbugs>
</target>
</project>
設置好Ant的環境後,在命令中使用ant -f build.xml,或者在Eclipse直接運行build.xml文件,運行後生成了一個xml文件,如果你想用Html的格式查看findbugs的結果,可以把output屬性設爲:html。這樣就可以通過Html來查看findbugs的結果了。
最簡單的例子如下:
<project name="findbugsproject" default="findbugs">
<property name="findbugs.home" value="findbugs-1.3.9" />
<taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"/>
<target name="findbugs">
<findbugs home="${findbugs.home}" output="html" outputFile="aaa.html" effort="default">
<class location="Client/classes" />
<sourcePath path="Client/src" />
</findbugs>
</target>
</project>
提供的Swing工具
Ant操作是專家級的操作,一般對於Java不是很熟悉的人,寫build.xml文件。比起Ant來,使用Findbugs提供的Swing工具會使Findbugs的操作更加簡單。運行Findbugs解壓包中的bin文件夾下的findbugs.bat文件。
Findbugs的Swing工具初始主界面如下:
findbugs-1.3.9.zip ----WINDOWS下載FindBugs軟件,可獨立運行.並可在Ant中使用FindBugs,這個下截文件解壓後目錄中有一個BAT文件,打開後就能看到SWING界面
在分析項目之前,我們必須要新建一個項目來分析,選擇文件->新建
顯示新建項目的界面如下圖:
然後添加要分析的類包和目錄(可以選擇編譯好的類所在的文件夾,也可以選擇生成的jar包),再添加輔助類所在的文件夾和源文件所在的文件夾(java文件所在的文件夾)。再點擊完成就可以建立一個要分析的項目。
建立項目後,會自動先自動開始解析項目。
解析後界面:
其中左邊是缺陷的樹結構列表,點擊其中一個Bug,可以在右邊的界面中,顯示Bugs的源文件以及所在的位置。
5. 命令行下使用FindBugs和圖形化的FindBugs的使用
此處不介紹這種使用方式,詳細內容參考findbugs manual。
6. FindBugs Bug描述
筆者認爲其實最重要的還是FindBugs可以幫助我們找出哪些Bugs。
但是FindBugs的Bug描述是在太多,可以參考:
http://findbugs.sourceforge.net/bugDescriptions.html
這個檢測器尋找與equals()和hashCode()的實現相關的幾個問題。這兩個方法非常重要,因爲幾乎所有基於集合的類—— List、Map、Set等都調用它們。一般來說,這個檢測器尋找兩種不同類型的問題——當一個類:
- 重寫對象的equals()方法,但是沒有重寫它的hashCode方法,或者相反的情況時。
- 定義一個 co-variant版本的equals()或compareTo()方法。例如,Bob類定義其equals()方法爲布爾equals(Bob),它覆蓋了對象中定義的equals()方法。因爲 Java 代碼在編譯時解析重載方法的方式,在運行時使用的幾乎總是在對象中定義的這個版本的方法,而不是在Bob中定義的那一個(除非顯式將equals()方法的參數強制轉換爲Bob類型)。因此,當這個類的一個實例放入到類集合中的任何一箇中時,使用的是Object.equals()版本的方法,而不是在Bob中定義的版本。在這種情況下,Bob類應當定義一個接受類型爲Object的參數的equals()方法。
這個檢測器查找代碼中忽略了不應該忽略的方法返回值的地方。這種情況的一個常見例子是在調用String方法時,如在清單 1中:
1 String aString = "bob";
2 b.replace('b', 'p');
3 if(b.equals("pop"))
|
這個檢測器查找兩類問題。它查找代碼路徑將會或者可能造成 null指針異常的情況,它還查找對 null的冗餘比較的情況。例如,如果兩個比較值都爲 null,那麼它們就是冗餘的並可能表明代碼錯誤。FindBugs在可以確定一個值爲 null而另一個值不爲 null時,檢測類似的錯誤,如清單 2所示:
1 Person person = aMap.get("bob");
2 if (person != null) {
3 person.updateAccessTime();
4 }
5 String name = person.getName();
|
這個檢測器尋找在構造函數中初始化之前被讀取的字段。這個錯誤通常是——儘管不總是如此——由使用字段名而不是構造函數參數引起的,如清單 3 所示:
1 public class Thing {
2 private List actions;
3 public Thing(String startingActions) {
4 StringTokenizer tokenizer = new StringTokenizer(startingActions);
5 while (tokenizer.hasMoreTokens()) {
6 actions.add(tokenizer.nextToken());
7 }
8 }
9 }
|
<taskdef name="FindBugs" classname="edu.umd.cs.FindBugs.anttask.FindBugsTask"/>
|
1 <target name="FindBugs" depends="compile">
2 <FindBugs home="${FindBugs.home}" output="xml" outputFile="jedit-output.xml">
3 <class location="c:/apps/JEdit4.1/jedit.jar" />
4 <auxClasspath path="${basedir}/lib/Regex.jar" />
5 <sourcePath path="c:/tempcbg/jedit" />
6 </FindBugs>
7 </target>
|
<property name="FindBugs.home" value="C:/apps/FindBugs-0.7.3" />
|
- 匹配一個類的過濾器。可以用這些過濾器忽略在特定類中發現的所有問題。
- 匹配一個類中特定缺陷代碼(bugcode)的過濾器。可以用這些過濾器忽略在特定類中發現的一些缺陷。
- 匹配一組缺陷的過濾器。可以用這些過濾器忽略所分析的所有類中的一組缺陷。
- 匹配所分析的一個類中的某些方法的過濾器。可以用這些過濾器忽略在一個類中的一組方法中發現的所有缺陷。
- 匹配在所分析的一個類中的方法中發現的某些缺陷的過濾器。可以用這些過濾器忽略在一組方法中發現的特定缺陷。
- 可以考慮將 FindBugs結果加入到源代碼管理(SCM)系統中。一般的經驗做法是不將編譯工件(artifact)放到SCM 系統中。不過,在這種特定情況下,打破這個規則可能是正確的,因爲它使您可以監視代碼質量隨時間的變化。
- 可以選擇將 XML結果轉換爲可以發送到團隊的網站上的 HTML報告。轉換可以用 XSL樣式表或者腳本實現。有關例子請查看 FindBugs網站或者郵件列表(請參閱參考資料)。
- 像 FindBugs這樣的工具通常會成爲用於敲打團隊或者個人的政治武器。儘量抵制這種做法或者不讓它發生——記住,它只是一個工具,它可以幫助改進代碼的質量。有了這種思想,在下一部分中,我將展示如何編寫自定義缺陷檢測器。
- 您可以參閱本文在 developerWorks全球站點上的英文原文.
- 下載最新版本的 FindBugs。
- FindBugs網站提供了完整的缺陷清單及說明。
- 閱讀有關Visitor模式的更多信息。
- 這裏有關於字節碼設計庫(Code Engineering Library)的更多信息。
- 可以在論文“Finding Bugs is Easy”及其源代碼中找到更多關於 FindBugs的細節。
- PMD是另一個強大的開放源代碼靜態代碼分析工具,它可以讓您編寫自定義規則。它不像 FindBugs那麼強大,因爲它分析 Java文件而不是類文件,但是仍然值得了解。
- 兩位作者試圖描述避免 FindBugs可以檢測到的那些問題的一組最佳實踐:Joshua Bloch的Effective Java: Programming Language Guide(Addison-Wesley,2001年)和 Peter Haggar的Practical Java: Programming Language Guide(Addison-Wesley,2000年)。
- 在“The future of software development” (developerWorks,2003年 6 月)中,Eric Allen討論了軟件開發中的一些趨勢,並預言未來幾年它們的方向。請查看 Eric的Diagnosing Java code columns的其他內容以瞭解常見的缺陷模式。
- Mark Roulo在其文章“Complement testing with code inspections” (developerWorks,2000年 3 月)中討論了通過代碼檢查可以檢測到的常見錯誤。其中一些錯誤也可由 FindBugs檢測。
- 在developerWorksJava技術專區找到數百篇 Java技術資料。 .
- 請訪問Developer Bookstore,獲取技術書籍的完整清單,包括數百本 Java相關主題的書籍。
詳解eclipse插件findbugs新規則的開發過程
java應用最常見的也就是NullPointException問題了。平時我們做小的項目出幾個NPE沒什麼太大的影響,打幾個錯誤日誌,下次修復掉就行了。但是如果是淘寶、支付寶這樣的大型系統,每天用戶量很大,可能一個NPE就會影響到很多用戶的系統使用。findbugs會容易的找出這些問題。
有的時候findbugs不能滿足我們的需求,我們需要在代碼掃描階段就發現更多的問題,那麼就需要開發針對自己需求的findbugs規則。比如:生產環境的代碼中是不允許有System.out.prinln("xxxxx");這樣的信息出現的,必須使用log來記錄日誌,所以我們就可以專門寫一條規則來檢測代碼裏面是否存在System.out,如果存在就給出提示。
同樣的,在使用log日誌的時候,必須要先判斷日誌的級別然後再使用log.debug(""),所以我們可以定義一條日誌來檢測代碼中是否存在沒有使用if條件判斷就直接log.debug(),有的話給出提示。
進入正題,通過找代碼中是否存在System.out來講解findbugs規則的開發過程
效果:
準備工作:
1 findbugs源碼的下載下載路徑:
http://code.google.com/p/findbugs/source/checkout 通過svn下載,svn命令: Svn checkouthttp://findbugs.googlecode.com/svn/trunk/ findbugs-read-only
2 將源碼導入eclipse
在eclipse中選擇import --- plug-ins and fragments,選擇下載的findbugs源碼的路徑import as選項卡中選擇 projects with source folders
添加plug-ins的時候記得不要選擇中間的那個,中間的是test,也可以選擇全導入
3 項目環境設置
在edu.umd.cs.findbugs.plugin.eclipse項目中找到plugin.xm用manifest editor打開,在build選項卡中add Library:findbugs-plugin.jar,選中findbugs-plugin.jar,add folder:src
在findbugs項目中找到MANIFEST.MF,在build中add Library:findbugs.jar,選中findbugs-plugin.jar,add folder:src/java,src/java5,src/tools,src/antTask
開發新規則:
1.首先認識幾個文件
Findbugs.xml
對於每一個新的檢測器,在 FindBugs.xml 文件中增加一個 Detector 元素和一個 BugPattern 元素。 Detector 元素指定用於實現檢測器的類以及它是快速還是慢速檢測器。其中reports屬性是和edu.umd.cs.findbugs.detect中類report的錯誤相對應的和Bugpattern中的type一致且唯一。
category 屬性是枚舉類型。
它是以下類型中的一種:
CORRECTNESS :一般正確性問題
MT_CORRECTNESS :多線程正確性問題
MALICIOUS_CODE :如果公開給惡意代碼,有可能成爲攻擊點
PERFORMANCE :性能問題
Message.xml
messages.xml 文件由三個元素組成: Detector 、 BugPattern 和 BugCode 。檢測器的 class 屬性應當指定檢測器的類名。 Details 元素包含檢測器的簡單 HTML 描述,這裏面主要寫錯誤的提示信息。
FindBugs 利用了 Byte Code Engineering Library,稱爲 BCEL,以實現其檢測器。所有字節碼掃描檢測器都基於 visitor 模式。側重於兩個方法------ visit(Code) 和 sawOpcode(int) 。在 FindBugs 分析類時,它會在分析方法內容時調用 visit(Code) 方法。與此類似,FindBugs 在分析方法正文中的每一個操作碼時調用 sawOpcode(int) 方法。
下面我們看一個列子:在企業級開發中,是不允許用System.out來輸出信息的,必須要用log日誌來打印出信息,所以我們就增加一個findbugs的新規則發現代碼中有system.out的時候就給用戶提示,一下是開發步驟
先看一段通過javap反編的java代碼對比
源碼:
Java代碼
- public class Test{
- public static void main(String[] args){
- String str="pass";
- if(str.equals("pass")){
- System.out.println("str is pass");
- }
- }
- }
- public class Test{
- public static void main(String[] args){
- String str="pass";
- if(str.equals("pass")){
- System.out.println("str is pass");
- }
- }
- }
反編:
Java代碼
- Compiled from "Test.java"
- public class Test extends java.lang.Object{
- public Test();
- Code:
- 0: aload_0
- 1: invokespecial #1; //Method java/lang/Object."<init>":()V
- 4: return
- public static void main(java.lang.String[]);
- Code:
- 0: ldc #2; //String pass
- 2: astore_1
- 3: aload_1
- 4: ldc #2; //String pass
- 6: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
- 9: ifeq 20
- 12: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
- 15: ldc #5; //String str is pass
- 17: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 20: return
- }
- Compiled from "Test.java"
- public class Test extends java.lang.Object{
- public Test();
- Code:
- 0: aload_0
- 1: invokespecial #1; //Method java/lang/Object."<init>":()V
- 4: return
- public static void main(java.lang.String[]);
- Code:
- 0: ldc #2; //String pass
- 2: astore_1
- 3: aload_1
- 4: ldc #2; //String pass
- 6: invokevirtual #3; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
- 9: ifeq 20
- 12: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
- 15: ldc #5; //String str is pass
- 17: invokevirtual #6; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
- 20: return
- }
通過反編的代碼我們可以看到調用system.out.println的時候是通過
12: getstatic #4; //Field java/lang/System.out:Ljava/io/PrintStream;
這句來執行的,所以我們只要找到getstatic指令,並判斷方法調用是System.out就可以知道是用了System.out,就可以聲明bug並且報告bug
findbugs代碼
Java代碼
- package edu.umd.cs.findbugs.detect;
- import org.apache.bcel.classfile.Code;
- import edu.umd.cs.findbugs.BugInstance;
- import edu.umd.cs.findbugs.BugReporter;
- import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
- /**
- * @author bo
- * 這個規則類用於判斷System.out和System.error這種情況
- */
- public class ForbiddenSystemClass extends OpcodeStackDetector {
- BugReporter bugReporter;
- public ForbiddenSystemClass(BugReporter bugReporter) {
- this.bugReporter = bugReporter;
- }
- /**
- * visit方法,在每次進入字節碼方法的時候調用
- * 在每次進入新方法的時候清空標誌位
- */
- @Override
- public void visit(Code obj) {
- super.visit(obj);
- }
- /**
- * 每掃描一條字節碼就會進入sawOpcode方法
- *
- * @param seen 字節碼的枚舉值
- */
- @Override
- public void sawOpcode(int seen) {
- if (seen == GETSTATIC) {
- if (getClassConstantOperand().equals("java/lang/System")
- && (getNameConstantOperand().equals("out") || getNameConstantOperand().equals("error"))) {
- BugInstance bug = new BugInstance(this, "ALP_SYSTEMCLASS", NORMAL_PRIORITY).addClassAndMethod(this)
- .addSourceLine(this, getPC());
- bug.addInt(getPC());
- bugReporter.reportBug(bug);
- }
- }
- }
- }
- package edu.umd.cs.findbugs.detect;
- import org.apache.bcel.classfile.Code;
- import edu.umd.cs.findbugs.BugInstance;
- import edu.umd.cs.findbugs.BugReporter;
- import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
- /**
- * @author bo
- * 這個規則類用於判斷System.out和System.error這種情況
- */
- public class ForbiddenSystemClass extends OpcodeStackDetector {
- BugReporter bugReporter;
- public ForbiddenSystemClass(BugReporter bugReporter) {
- this.bugReporter = bugReporter;
- }
- /**
- * visit方法,在每次進入字節碼方法的時候調用
- * 在每次進入新方法的時候清空標誌位
- */
- @Override
- public void visit(Code obj) {
- super.visit(obj);
- }
- /**
- * 每掃描一條字節碼就會進入sawOpcode方法
- *
- * @param seen 字節碼的枚舉值
- */
- @Override
- public void sawOpcode(int seen) {
- if (seen == GETSTATIC) {
- if (getClassConstantOperand().equals("java/lang/System")
- && (getNameConstantOperand().equals("out") || getNameConstantOperand().equals("error"))) {
- BugInstance bug = new BugInstance(this, "ALP_SYSTEMCLASS", NORMAL_PRIORITY).addClassAndMethod(this)
- .addSourceLine(this, getPC());
- bug.addInt(getPC());
- bugReporter.reportBug(bug);
- }
- }
- }
- }
new BugInstance(this, "ALP_SYSTEMCLASS", NORMAL_PRIORITY)
ALP_SYSTEMCLASS這個和findbugs.xml、message.xml中相對應
findbugs的新規則開發使用了visit模式,我們只需要實現visit方法sawOpcode方法即可,當然實現複雜功能,有不同的父類
在findbugs.xml中把自己的Detector 聲明出來
Xml代碼
- <Detector class="edu.umd.cs.findbugs.detect.AlipayForbiddenSystemClass"
- speed="fast"
- reports="ALP_SYSTEMCLASS"
- hidden="false" />
- <Detector class="edu.umd.cs.findbugs.detect.AlipayForbiddenSystemClass"
- speed="fast"
- reports="ALP_SYSTEMCLASS"
- hidden="false" />
message.xml
- <Detector class="edu.umd.cs.findbugs.detect.ForbiddenSystemClass">
- <Details>
- <!--[CDATA[
- <p>線上代碼不能出現System.out
- <p>請使用log日誌形式打印
- ]]>
- </Details>
- </Detector>
- <BugPattern type="ALP_ALIPAY_SYSTEMCLASS">
- <ShortDescription>線上代碼不能出現System.out</ShortDescription>
- <LongDescription>{1}線上代碼不能出現System.out,請使用log形式輸出</LongDescription>
- <Details>
- <![CDATA[
- <p>不能使用System.out和System.err,請使用log</p>
- ]]-->
- </Details>
- </BugPattern>
- <Detector class="edu.umd.cs.findbugs.detect.ForbiddenSystemClass">
- <Details>
- <!--[CDATA[
- <p>線上代碼不能出現System.out
- <p>請使用log日誌形式打印
- ]]>
- </Details>
- </Detector>
- <BugPattern type="ALP_ALIPAY_SYSTEMCLASS">
- <ShortDescription>線上代碼不能出現System.out</ShortDescription>
- <LongDescription>{1}線上代碼不能出現System.out,請使用log形式輸出</LongDescription>
- <Details>
- <![CDATA[
- <p>不能使用System.out和System.err,請使用log</p>
- ]]-->
- </Details>
- </BugPattern>
這裏配置錯誤的顯示信息
最終把
java類、xml按照下面這個ant腳本的描述進行打包
- <project name="findbugs-plugin" default="build">
- <property name="FindBugs.home" value="D:/Program Files/eclipse/plugins/edu.umd.cs.findbugs.plugin.eclipse_1.3.8.20090315"></property>這裏是你的eclipse中findbugs的路徑
- <target name="build">
- <jar destfile="AlipayFindBugsRules.jar">
- <fileset dir="bin"/>
- <fileset dir="src"/>
- <zipfileset dir="etc" includes="*.xml" prefix=""></zipfileset>
- </jar>
- <copy file="FindBugsRules.jar" todir="${FindBugs.home}/plugin" />
- </target>
- </project>
- <project name="findbugs-plugin" default="build">
- <property name="FindBugs.home" value="D:/Program Files/eclipse/plugins/edu.umd.cs.findbugs.plugin.eclipse_1.3.8.20090315"></property>這裏是你的eclipse中findbugs的路徑
- <target name="build">
- <jar destfile="AlipayFindBugsRules.jar">
- <fileset dir="bin"/>
- <fileset dir="src"/>
- <zipfileset dir="etc" includes="*.xml" prefix=""></zipfileset>
- </jar>
- <copy file="FindBugsRules.jar" todir="${FindBugs.home}/plugin" />
- </target>
- </project>
命令行ant就打包了,把打好的jar包放到findbugs插件的plugin目錄下,重啓eclipse就可以使用新的規則了
其它分析工具
除FingBugs靜態分析工具外,還有PMD和Checkstyle,FingBugs、PMD和Checkstyle三個工具各有不同的特點,聯合使用有助於減少誤報錯誤,提高報告的準確率。
這三個工具檢查的側重點各有不同:
工具 |
目的 |
主要檢查內容 |
FindBugs |
基於Bug Patterns概念,查找java bytecode中的潛在bug。在目前版本中,它不檢查java源文件。 |
主要檢查bytecode中的bug patterns,也允許用戶自定義特定的bug patterns。 |
PMD |
檢查java源文件中的潛在問題。 |
主要包括: - 空try/catch/finally/switch語句塊 - 未使用的局部變量、參數和private方法 - 空if/while語句 - 過於複雜的表達式,如不必要的if語句等 - 複雜類 |
CheckStyle |
檢查java源文件是否與代碼規範相符 |
主要包括 - Javadoc註釋 - 命名規範 - Headers - Imports - Size衝突和度量,如過長的方法 - Whitespace - Modifiers - Blocks - Coding Problems - Class Design - 重複代碼 - Miscellaneous Checks - Optional Checks |