网上介绍Findbugs的自定义检测器的资料太少了。只是很少的一些网友发的文章。特别是对于里面的API的介绍几乎没有。还好了,有源代码,只有自己研究下了。刚好最近开发插件,稍稍研究了下里面的源代码,实现了单例的线程安全检测插件。
1 检测器
1.1 BytecodeScanningDetector
对于扫描字节码的需求,一般是扩展这个类。
1.1.1 Visit
Visit有很多不同参数的方法。表示访问类、或者代码、方法等时候会调用该方法。
一般用该方法进行访问前的初始化工作。
l public void visit(Code obj)
分析方法内容时调用visit(Code)方法,往往用于分析方法代码前进行初始化工作
l public void visit(JavaClass obj)
分析该类之前,调用该方法。往往用于取得类的信息
l public void visitField(Field obj)
分析类的属性前,调用该方法,往往用于取得类的属性信息。
例如:
public void visit(JavaClass obj) {
isHasField = false;
isPublicStaticMethord = false;
super.visit(obj);
}
public void visitField(Field obj) {
if (isHasField) {
return;
}
//如果存在非基本类型非最终属性非静态的
//那么肯能就是类的属性了
if (!obj.isFinal() && !obj.isStatic()) {
isHasField = true;
}
}
1.1.2 VisitAfter
public void visitAfter(JavaClass obj)
类分析只后调用该方法
1.1.3 sawXXX
sawXXX这类方法都会在分析XXX的时候调用该方法。
1.1.3.1 sawOpcode
public void sawOpcode(int seen)
分析字节码
>>在分析方法正文中的每一个操作码时调用sawOpcode(int)方法。
>> seen就是每条的操作码,操作码在反编译后都能看到
指令码都是该类的常量,可以找到。
汇编指令码如下:
public void show();
Code:
0: getstatic #29; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #48; //String ssssssssssssss00s
5: invokevirtual #35; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
8: return
>>取得该指令对于的类
getClassConstantOperand()
对于5:可以取到java/io/PrintStream
>>取得该指令对应的类执行的方法的名字
getNameConstantOperand()
对于5:可以取到println
>>例如
汇编代码:
public void doBadStuff();
Code:
0: invokestatic #2; //Method java/lang/System.gc:()V
3: return
查找调用了System.gc的代码
public void sawOpcode(int seen) {
if (seen == INVOKESTATIC) {
if (getClassConstantOperand().equals("java/lang/System")
&& getNameConstantOperand().equals("gc")) {
bugReporter.reportBug(new BugInstance("SYSTEM_GC", NORMAL_PRIORITY)
.addClassAndMethod(this)
.addSourceLine(this));
}
}
}
}
1.1.3.2 sawMethod
public void sawMethod()
>>每分析一个方法前,都会调用该方法
>>取得类的名字
String className = super.getClassName().replaceAll("/", ".");
>>取得方法的名字
this.getMethod().getName()
>>取得方法的返回类型
String returnType = this.getMethod().getReturnType().toString();
>>取得方法是否为静态
boolean isStatic = this.getMethod().isStatic();
>>取得方法是否为公开的
boolean isPublic = this.getMethod().isPublic();
>>例如:
public void sawMethod() {
if (isPublicStaticMethord) {
return;
}
//class name: demo/First|| methord name :show|| ReturnType() name : //demo.Second //accce flag= 1
String className = super.getClassName().replaceAll("/", ".");
String returnType = this.getMethod().getReturnType().toString();
boolean isStatic = this.getMethod().isStatic();
boolean isPublic = this.getMethod().isPublic();
//单例判断
if (isPublic && isStatic) {//如果为公有的静态的
if (className != null && className.equals(returnType)) {//如果返回值就是本类
isPublicStaticMethord = true;
}
}
}
1.1.4 生成报表
要生成报表,需要几步操作
1、构造函数里面会传递报表参数
publicSingletonDector(BugReporter bugReporter) {
this.bugReporter= bugReporter;
}
2.在恰当的地方调用报表
一般是在visitXXX sawXXX地方检查的时候调用
bugReporter.reportBug(newBugInstance("MULTITHREAD_SINGLETON",NORMAL_PRIORITY)
.addClass(this))
;
>>MULTITHREAD_SINGLETON
就是配置的报表的名字
>>addXXX
就是传递给报表的属性,在报表里面是可以取到的
--------------------------2010-4-1 river.liu 转载请保证完整