幹貨:分分鐘幹掉 Bug 之靈活地運用 Findbugs 過濾器
時間 2016-11-18 15:52:47 ITKobe24’s Blog 原文 https://dreamerhome.github.io/2016/11/18/finbugs/ 主題 Java
Findbugs是一款比較成熟的靜态代碼檢查工具,能夠友善準确地檢查出代碼中存在的一些問題,檢查結果簡潔明了,檢查規則的可配置性也非常高,在生産環境中可以很方面的快速解決一些潛在的問題。然而預設的Findbugs給出的報告存在很多我們不關心的問題,或者說對我們業務代碼基本沒有影響的警告,如果不對這些内容設定過濾很大程度上會加大我們的排查成本。下面就來簡單介紹下使用和配置方法以及如何在結合我們的開發過程來使用。
添加Findbugs任務
在gradle中加入下面的内容即可快速添加findbugs的task
apply plugin: 'findbugs'
task findbugs(type: FindBugs)
{
//excludeFilter file('findbugs-filter.xml')
classes = fileTree('build/intermediates/classes/debug')
source = fileTree('src/main/java/')
classpath = files()
effort = 'max'
reportLevel = 'medium'
ignoreFailures = true
reports {
//隻能開啟一個
xml.enabled false
html.enabled true
html.stylesheet resources.text.fromFile('findbugs-template.xsl')
}
}
- effort 可以設定成max和min,一般在記憶體和時間不緊張的情況下都用max
- reportLevel 檢測級别,可以設定成low,medium和high,預設是medium
- reports設定生成的報告類型和位址,隻能選擇xml或者html中的一種
設定過濾器
先來看看findbugs給的報告:
findbugs預設給的報告中有許多類型的警告是我們不關心的,是以我們需要設定一個過濾器來接受我們關注的内容過濾掉不需要的警告
excludeFilter就是用來配置過濾器的,過濾内容則通過findbugs-filter.xml檔案來約定,也是findbugs配置中最重要的一個環節。
findbugs的檢查報告包括了分好幾大類型的警告(Warning Type),如圖中的Bad prictice Warnings,Internationalization Warnings等類型,每個類型下又有其他的代碼警告(Code Warning),如圖中 的code碼Nm和後面的介紹,當然其中有很多我們不關心的警告Code,比如Eq(建議用equals()來代替==進行比較的警告)UrF(提示Activity中的控件如TextView沒有初始化,其實我們是通過注入初始化的)等在某些類型的警告中是我們不關注的,需要過濾掉。接下來簡單說一下如何配置過濾規則。
過濾标簽介紹
- <Bug> 設定警告的類型
- <Confidence>設定過濾等級
- <Package>設定過濾包名
- <Class>設定過濾的類名
- <Source>設定過濾的源檔案名
- <Method>設定過濾的方法名
過濾組合操作符:
- <Or>或
- <And>與
- <Not>非
過濾器設定示例
這裡把國際化相關的警告全部過濾掉了
<!-- code flaws having to do with internationalization and locale -->
<Match>
<Bug category="I18N" />
</Match>
有的時候我們可能隻想過濾掉某個大的類别中的幾個小的類型的警告,比如過濾掉Performence中的UrF
<Match>
<And>
<Bug category="PERFORMANCE" />
<Bug code="SIC,UrF,UuF" />
</And>
</Match>
過濾某個特定的類:
<Match>
<Class name="com.foobar.MyClass" />
</Match>
或者是類中的特定某些警告類型
<Match>
<Class name="com.foobar.MyClass"/ >
<Bug code="DE,UrF,SIC" />
</Match>
全局過濾某些警告類型:
<Match>
<Bug code="DE,UrF,SIC" />
</Match>
運用組合條件過濾某個類中的特定内容警告:
<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>
當然用法可以很靈活,能夠非常友善的定制符合業務需求的過濾器 ,進而達到最高效解決我們關注問題的目的。
擷取報告檔案
配置好過濾器之後隻要在指令行敲入gradle findbugs即可得到一份報告文檔,接下來就可以開心地解決問題了
如何結合開發環境使用
這是最後比較關鍵的一點,前面的内容都是為了最後友善我們解決問題,是以在這裡建議在自己分支開發完相關的功能之後就可以運作findbugs來檢視是否自己在開發新需求的時候産生了可能的問題,如果報告内容有提示則需要把相關的 問題給解決了再送出代碼。當然報告的内容不一定都是需要解決的問題,如果你認為相關的警告實際中是沒有必要考慮的可以自行在配置檔案中過濾掉就行了,保證最後送出的代碼是沒有報告檔案産生的(即在自己分支上運作findbugs最後沒有報告檔案生成說明我們關注的問題全都解決了)那麼就大功告成。
附錄
Warning Type 與Bug Category的對應關系
當我們想對某種大的類型的警告進行過濾時可以使用該轉換表,如過濾掉所有與國際化相關的警告(Internationalization Warnings):
<Match>
<Bug category="I18N" />
</Match>
The bug categories can be disabled by specifying the category attribute in the exclusion file:
- Internationalization Warnings: I18N
- Malicious code Warnings: MALICIOUS_CODE
- Experimental Warnings: EXPERIMENTAL
- Correctness Warnings: CORRECTNESS
- Performance Warnings: PERFORMANCE
- Dodgy Code Warnings: STYLE
- Bad practice Warnings: BAD_PRACTICE
FindBugs的一些Bug種類說明(全部說明請參考: http://findbugs.sourceforge.net/bugDescriptions.html)
Bad practice 糟糕的代碼實作:
- HE: 類定義了equals(),卻沒有hashCode();或類定義了equals(),卻使用Object.hashCode();或類定義了hashCode(),卻沒有equals();或類定義了hashCode(),卻使用Object.equals();類繼承了equals(),卻使用Object.hashCode()。
- SQL:Statement 的execute方法調用了非常量的字元串;或Prepared Statement是由一個非常量的字元串産生。
- DE: 方法終止或不處理異常,一般情況下,異常應該被處理或報告,或被方法抛出。
Malicious code vulnerability 存在安全隐患的代碼
如果代碼公開,可能受到惡意攻擊的代碼,下面列舉幾個:
- FI: 一個類的finalize()應該是protected,而不是public的。
- MS:屬性是可變的數組;屬性是可變的Hashtable;屬性應該是package protected的。
Correctness 可能出錯的代碼
可能導緻錯誤的代碼,下面列舉幾個:
- NP: 空指針被引用;在方法的異常路徑裡,空指針被引用;方法沒有檢查參數是否null;null值産生并被引用;null值産生并在方法的異常路徑被引用;傳給方法一個聲明為@NonNull的null參數;方法的傳回值聲明為@NonNull實際是null。
- Nm: 類定義了hashcode()方法,但實際上并未覆寫父類Object的hashCode();類定義了tostring()方法,但實際上并未覆寫父類Object的toString();很明顯的方法和構造器混淆;方法名容易混淆。
- SQL:方法嘗試通路一個Prepared Statement的0索引;方法嘗試通路一個ResultSet的0索引。
- UwF:所有的write都把屬性置成null,這樣所有的讀取都是null,這樣這個屬性是否有必要存在;或屬性從沒有被write。
Dodgy Code 存在潛在危險的代碼
具有潛在危險的代碼,可能運作期産生錯誤,下面列舉幾個:
- CI: 類聲明為final但聲明了protected的屬性。
- DLS:對一個本地變量指派,但卻沒有讀取該本地變量;本地變量指派成null,卻沒有讀取該本地變量。
- ICAST: 整型數字相乘結果轉化為長整型數字,應該将整型先轉化為長整型數字再相乘。
- INT:沒必要的整型數字比較,如X <= Integer.MAX_VALUE。
- NP: 對readline()的直接引用,而沒有判斷是否null;對方法調用的直接引用,而方法可能傳回null。
- REC:直接捕獲Exception,而實際上可能是RuntimeException。
- ST: 從執行個體方法裡直接修改類變量,即static屬性。
Performance 性能不佳的代碼
可能導緻性能不佳的代碼,下面列舉幾個:
- DM:方法調用了低效的Boolean的構造器,而應該用Boolean.valueOf(…);用類似Integer.toString(1) 代替new Integer(1).toString();方法調用了低效的float的構造器,應該用靜态的valueOf方法。
- SIC:如果一個内部類想在更廣泛的地方被引用,它應該聲明為static。
- SS: 如果一個執行個體屬性不被讀取,考慮聲明為static。
- UrF:如果一個屬性從沒有被read,考慮從類中去掉。
- UuF:如果一個屬性從沒有被使用,考慮從類中去掉。
Multithreaded correctness 多線程不安全的代碼
多線程程式設計時,可能導緻錯誤的代碼,下面列舉幾個:
- ESync:空的同步塊,很難被正确使用。
- MWN:錯誤使用notify(),可能導緻IllegalMonitorStateException異常;或錯誤的使用wait()。
- No: 使用notify()而不是notifyAll(),隻是喚醒一個線程而不是所有等待的線程。
- SC: 構造器調用了Thread.start(),當該類被繼承可能會導緻錯誤。
</div>
<div class="article_social">
<div class="article_like">
<div class="circle circle-like" id="my_zan" data_id="RnQniaI"> </div>
分享 收藏 糾錯 ("#share_weibo_id").click( function() {
window.open("http://share.baidu.com/s?type=text&searchPic=0&sign=on&to=tsina&url=http://www.tuicool.com/articles/RnQniaI&title=+%E5%B9%B2%E8%B4%A7%EF%BC%9A%E5%88%86%E5%88%86%E9%92%9F%E5%B9%B2%E6%8E%89+Bug+%E4%B9%8B%E7%81%B5%E6%B4%BB%E5%9C%B0%E8%BF%90%E7%94%A8+Findbugs+%E8%BF%87%E6%BB%A4%E5%99%A8++%28%E5%88%86%E4%BA%AB%E8%87%AA+%40%E6%8E%A8%E9%85%B7%E7%BD%91%29&key=3113829255");
});("#share_qq_id").click( function() { window.open("http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=http://www.tuicool.com/articles/RnQniaI&title=+%E5%B9%B2%E8%B4%A7%EF%BC%9A%E5%88%86%E5%88%86%E9%92%9F%E5%B9%B2%E6%8E%89+Bug+%E4%B9%8B%E7%81%B5%E6%B4%BB%E5%9C%B0%E8%BF%90%E7%94%A8+Findbugs+%E8%BF%87%E6%BB%A4%E5%99%A8&desc=&summary=&site="); }); ("#share_weixin_id").click( function() {("#share_weixin_image").toggle(); });
</div>
<div id="site_articles" style="clear:both;">
<div class="article-part-title">
<span>推薦文章</span>
</div>
<ul class="side_article_list">
<li class="side_article_list_item">
1.<a href="/articles/zaQrM3m" target="_blank" rel="external nofollow" target="_blank" title="Ivy入門:Eclipse整合Ivy">
Ivy入門:Eclipse整合Ivy
</a>
</li>
<li class="side_article_list_item">
2.<a href="/articles/RzAbMb6" target="_blank" rel="external nofollow" target="_blank" title="關于 Java 9 模闆系統特性被委員會拒絕詳情">
關于 Java 9 模闆系統特性被委員會拒絕詳情
</a>
</li>
<li class="side_article_list_item">
3.<a href="/articles/I7jqAfJ" target="_blank" rel="external nofollow" target="_blank" title="Eclipse中maven環境使用jetty啟動後不能儲存更改後的css,js等靜态檔案的解決方法">
Eclipse中maven環境使用jetty啟動後不能儲存更改後的css,js等靜态..
</a>
</li>
<li class="side_article_list_item">
4.<a href="/articles/ZbE7BzR" target="_blank" rel="external nofollow" target="_blank" title="log4j 日志資訊的引入(通用版)—— 解決項目運作過程中的日志資訊">
log4j 日志資訊的引入(通用版)—— 解決項目運作過程中的日志資訊
</a>
</li>
</ul>
</div>
<div id="kan_articles"> <div class="article-part-title"> <span>相關推刊</span></div><div class="kan-list-container"><ul class="kan-list clearfix"> <li class="kan-item"> <a href="/kans/2178302136" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" class="kan-item-head"> <small>by 妳の莎</small> <img class="kan-cover" src="http://img0.tuicool.com/ymENRjm.jpg!kan"> </a> <span class="kan-detail"> <a href="/kans/2178302136" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank">《1》</a> <i class="kan-num">25</i> </span> </li> <li class="kan-item"> <span class="kan-item-head"> <small></small> <img class="kan-cover" src="http://img0.tuicool.com/QFRbauz.png!kan"> </span> <span class="kan-detail"> <i>《匿名收藏》</i> <i class="kan-num">60</i> </span> </li> <li class="kan-item"> <span class="kan-item-head"> <small></small> <img class="kan-cover" src="http://img0.tuicool.com/zM7FNnn.jpg!kan"> </span> <span class="kan-detail"> <i>《匿名收藏》</i> <i class="kan-num">38</i> </span> </li> </ul></div><i class="clearfix"></i></div>
<div id="article_weibo" style="display:none;">
<div class="article-part-title">
<span>相關微網誌</span>
<sub>
<a href="/articles/weibo_list/RnQniaI" target="_blank" rel="external nofollow" target="_blank">(<i id="weibo_num"></i>)</a>
</sub>
</div>
<div class="related-weibo-list"></div>
</div>
<div class="comments">
<div class="comments-area">
<div class="comments-header">
<h5>我來評幾句</h5>
<div class="alert comment-alert alert-error" style="display:none;">
錯誤
</div>
<textarea cols="60" rows="5" id="comment-body" placeholder="請輸入評論内容..." style="resize: none;"></textarea>
<span class="btn btn-medium btn-submit" id="comment-submit">登入後評論</span>
<p style="margin-top: 5px;margin-left:10px;">
已發表評論數(<span class="comment_cnt">0</span>)
</p>
</div>
<div class="comments-list">
<div class="empty-cmts alert alert-success" style="display:none;">
沒有更多評論了^^
</div>
</div>
<div class="more-comments" style="display:none;">
<a href="" target="_blank" rel="external nofollow" >更多評論</a>
</div>
<div class="load-fail" style="display:none;">
評論加載失敗,<a href="javascript:reload_comments('RnQniaI',1,0,-1);" target="_blank" rel="external nofollow" >重新加載</a>
</div>
</div>
</div>