在 FindBugs 檢測(cè)器實(shí)現(xiàn)(1) 中提到,F(xiàn)indBugs 主要有5類檢測(cè)器,這篇日志介紹下FindBugs在類、方法、字段結(jié)構(gòu)上的檢測(cè)器實(shí)現(xiàn)。前面提到基于棧和簡(jiǎn)單的字節(jié)碼模式要繼承OpcodeStackDetector類,并實(shí)現(xiàn)sawOpcode方法用來(lái)檢測(cè)每一個(gè)字節(jié)碼。
一般在寫檢測(cè)器之前,我們應(yīng)該有一個(gè)自己想要檢測(cè)的代碼模式,但作為學(xué)習(xí),這里使用一些簡(jiǎn)單的模式作為例子:
- 重寫了equals函數(shù),卻沒(méi)有重寫hashCode函數(shù)。
- 經(jīng)hashCode函數(shù)拼寫為hashcode。
FindBugs在類、方法、字段層面的檢測(cè)器實(shí)現(xiàn)要繼承PreorderDetector類,簡(jiǎn)單介紹些該類:
?
PreorderVisitor
PreorderVisitor 繼承了 BetterVisitor,并在 BetterVisitor的基礎(chǔ)之上,添加了大量的方法用來(lái)從當(dāng)前訪問(wèn)的類、方法或字段中獲取信息,另外該類還提供了一些visitAfter方法,用來(lái)實(shí)現(xiàn)訪問(wèn)上的次序,在下面的例子中,我們會(huì)看到它的作用。像下面的方法 PreorderVisitor 中存在太多,這里就不一一列出,可能有些方法不能直接明了的知道其作用,大多可能是我們還不懂java的一些功能的緣故:
public
FieldDescriptor getFieldDescriptor()
public
MethodDescriptor getMethodDescriptor()
public
ClassDescriptor getClassDescriptor()
public
@SlashedClassName String getClassName()
public
void
visitAfter(Code obj)
public
void
visitAfter(JavaClass obj)
?
來(lái)個(gè)例子
public
class
DemoPlugin
extends
PreorderDetector {
private
BugReporter bugReporter;
public
DemoPlugin(BugReporter reporter){
bugReporter
=
reporter;
}
public
boolean
hasHashcode;
public
boolean
hasEqual;
public
MethodAnnotation equals;
@Override
public
void
visit(JavaClass obj){
System.out.println(
"visit class!!!!!!!!!" +
obj.getClassName());
hasHashcode
=
false
;
hasEqual
=
false
;
equals
=
null
;
}
@Override
public
void
visit(Method me){
if
(!
me.isPublic())
return
;
if
("equals".equals(me.getName()) && "(Ljava/lang/Object;)Z"
.equals(me.getSignature())){
hasEqual
=
true
;
System.out.println(
"find equals() method"
);
equals
= MethodAnnotation.fromVisitedMethod(
this
);
}
if
("hashCode".equals(me.getName()) && "()I"
.equals(me.getSignature())){
hasHashcode
=
true
;
System.out.println(
"find hashCode() method"
);
}
if
("hashcode".equals(me.getName()) && "()I"
.equals(me.getSignature())){
bugReporter.reportBug(
new
BugInstance(
this
, "HASHCODE WRONG SPELL"
, NORMAL_PRIORITY).
addClass(getDottedClassName()).addMethod(MethodAnnotation.fromVisitedMethod(
this
)));
}
}
@Override
public
void
visitAfter(JavaClass obj){
if
(hasEqual && !
hasHashcode){
BugInstance instance
=
new
BugInstance(
this
, "EQUALS WITHOUT HASHCODE"
, NORMAL_PRIORITY).
addClass(getDottedClassName()).addMethod(equals);
bugReporter.reportBug(instance);
}
}
}
public
class
DemoPlugin
extends
PreorderDetector {
private
BugReporter bugReporter;
public
DemoPlugin(BugReporter reporter){
bugReporter
=
reporter;
}
public
boolean
hasHashcode;
public
boolean
hasEqual;
public
MethodAnnotation equals;
@Override
public
void
visit(JavaClass obj){
System.out.println(
"visit class!!!!!!!!!" +
obj.getClassName());
hasHashcode
=
false
;
hasEqual
=
false
;
equals
=
null
;
}
@Override
public
void
visit(Method me){
if
(!
me.isPublic())
return
;
if
("equals".equals(me.getName()) && "(Ljava/lang/Object;)Z"
.equals(me.getSignature())){
hasEqual
=
true
;
System.out.println(
"find equals() method"
);
equals
= MethodAnnotation.fromVisitedMethod(
this
);
}
if
("hashCode".equals(me.getName()) && "()I"
.equals(me.getSignature())){
hasHashcode
=
true
;
System.out.println(
"find hashCode() method"
);
}
if
("hashcode".equals(me.getName()) && "()I"
.equals(me.getSignature())){
bugReporter.reportBug(
new
BugInstance(
this
, "HASHCODE WRONG SPELL"
, NORMAL_PRIORITY).
addClass(getDottedClassName()).addMethod(MethodAnnotation.fromVisitedMethod(
this
)));
}
}
@Override
public
void
visitAfter(JavaClass obj){
if
(hasEqual && !
hasHashcode){
BugInstance instance
=
new
BugInstance(
this
, "EQUALS WITHOUT HASHCODE"
, NORMAL_PRIORITY).
addClass(getDottedClassName()).addMethod(equals);
bugReporter.reportBug(instance);
}
}
}
這里我們繼承了PreorderDetector,而PreorderDetector繼承了PreorderVisitor,是因?yàn)锽ugInstance需要使用Detector進(jìn)行實(shí)例,而PreorderDetector實(shí)現(xiàn)了Detector接口。
上面方法我們使用了上個(gè)變量hasHashcode,hasEquals和equals,前兩個(gè)變量用來(lái)保存當(dāng)前訪問(wèn)的類是否存在hashCode和equals方法,第三個(gè)變量存放的是方法注釋(要在報(bào)告bug中,這里先不管)。
上述三個(gè)變量每次都在visit(JavaClass obj)方法中進(jìn)行狀態(tài)重置,這樣才能對(duì)所有的類都進(jìn)行檢測(cè)。visit(Method me)方法中用來(lái)判斷是否是equals、hashCode、hashcode方法,并修改相應(yīng)的狀態(tài)。如果是hashcode方法,則直接將該bug報(bào)告。
visitAfter(JavaClass obj)方法執(zhí)行時(shí),已經(jīng)對(duì)當(dāng)前類檢測(cè)完畢,這樣可以判斷是否出現(xiàn)equals方法,而沒(méi)有出現(xiàn)hashCode方法的情況。
將fandbugs.xml和massage.xml加到項(xiàng)目中打包,便能對(duì)我們定義的模式進(jìn)行檢測(cè)。
以上僅是一個(gè)自定義在類、方法、字段層面上的檢測(cè)器的一般步驟,例子僅是為了說(shuō)明過(guò)程,只要我們熟悉我們想要檢測(cè)的模式,就能通過(guò)上述方法實(shí)現(xiàn)。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元

