findbugs工具是通过静态分析class文件来查找代码不合理项的工具,本文从通过Main方法调用和与maven集成两个方面来介绍findbugs的使用。
一、main方法集成
所有的java应用的主入口都是main方法,findbugs的main方法类为edu.umd.cs.findbugs.FindBugs2,因此我们在maven中添加对findbugs的依赖就可以直接调用其main方法。
<dependency> <groupId>com.google.code.findbugs</groupId> <artifactId>findbugs</artifactId> <version>3.0.1</version> <type>jar</type> </dependency>
调用main方法代码如下:
public static void main(String[] args) throws Exception { List<String> argsList = new ArrayList<String>(); argsList.add("-help"); //可以查看findbugs支持的选项,即用法说明 argsList.add("-html"); argsList.add("-output"); argsList.add("d:\\test.html"); argsList.add("../com.ozl.project/target/classes"); FindBugs2.main(argsList.toArray(new String[argsList.size()])); }
1、filter
findbugs提供了强大的filter能力,例如有些类可能是测试类,需要屏蔽掉,可以通过-exclude参数来控制过滤的范围,filter的内容参考如下:http://findbugs.sourceforge.net/manual/filter.html
我们的样例中只过滤其中的某些类,
<?xml version="1.0" encoding="UTF-8"?> <FindBugsFilter> <!-- android-support-v4.jar:包过滤 --> <Match> <Package name="~android\.support\.v4.*" /> <!-- 以~开头的表示正则表达式 --> </Match> <!-- 类过滤、方法 --> <Match> <Class name="com.ozl.project.ProblemGroup" /> </Match> <!-- Match all doublecheck violations in these methods of "AnotherClass". --> <Match> <Class name="com.opencdk.AnotherClass" /> <Or> <Method name="nonOverloadedMethod" /> <Method name="frob" params="int,java.lang.String" returns="void" /> <Method name="blat" params="" returns="boolean" /> </Or> <Bug code="DC" /> </Match> <!-- All bugs in test classes, except for JUnit-specific bugs --> <Match> <Class name="~.*\.*Test" /> <Not> <Bug code="IJU" /> </Not> </Match> </FindBugsFilter>
在main方法中只需要添加-exclude参数,并指定filter.xml文件即可
2、自定义规则
有的时候需要自定义增加规则,因为findbugs扫描的是class文件,因此需要对class文件有一定的了解才能写出自定义规则的代码,本文选取网上常见的例子说明,即禁止使用System.out.println类为例来说明如何使规则生效
首先需要编写探测器类:
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector; /** * @category 代码中避免使用有类似System.out的输出语句 */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")) { if(getNameConstantOperand().equals("out") || getNameConstantOperand() .equals("err")){ BugInstance bug = new BugInstance(this, "CJ_SYSTEMCLASS", NORMAL_PRIORITY).addClassAndMethod(this).addSourceLine( this, getPC()); bugReporter.reportBug(bug); } } } } }
编写findbugs.xml文件,并放到src/main/resources目录下
<FindbugsPlugin> <Detector class="com.ozl.findbugs.ForbiddenSystemClass" speed="fast" reports="CJ_SYSTEMCLASS" hidden="false" /> <BugPattern abbrev="CJ_SYSTEMCLASS" type="CJ_SYSTEMCLASS" category="PERFORMANCE" /> </FindbugsPlugin>
编写messages.xml文件(注意文件名字,必须同名,否则加载不到),并放到src/main/resources目录下
<?xml version="1.0" encoding="UTF-8"?> <MessageCollection> <Plugin> <ShortDescription>Default FindBugs plugin</ShortDescription> <Details> <![CDATA[ <p> This plugin contains all of the standard FindBugs detectors. </p> ]]> </Details> </Plugin> <Detector class="com.ozl.findbugs.ForbiddenSystemClass"> <Details> <![CDATA[ <p>代码不能出现System.out <p>请使用log日志形式打印 ]]> </Details> </Detector> <BugPattern type="CJ_SYSTEMCLASS"> <ShortDescription>代码不能出现System.out</ShortDescription> <LongDescription>{1}代码不能出现System.out,请使用log形式输出</LongDescription> <Details> <![CDATA[ <p>不能使用System.out和System.err,请使用log</p> ]]> </Details> </BugPattern> <BugCode abbrev="CJ_SYSTEMCLASS">影响性能的输出System.out</BugCode> </MessageCollection>
然后打成一个jar包,使用-pluginList来指定jar包。main方法如下:
public static void main(String[] args) throws Exception { List<String> argsList = new ArrayList<String>(); // argsList.add("-help"); // 可以查看findbugs支持的选项,即用法说明 argsList.add("-html"); argsList.add("-output"); argsList.add("d:\\test.html"); argsList.add("-exclude"); argsList.add("filter.xml"); argsList.add("-pluginList"); argsList.add("../com.ozl.findbugs/target/com.ozl.findbugs-1.0.0.jar"); argsList.add("../com.ozl.project/target/classes"); FindBugs2.main(argsList.toArray(new String[argsList.size()])); }
然后开心的运行main方法,但是却发现我们的规则并没有生效,什么鬼?
通过定位代码发现,需要对插件指定rank文件才行,默认的rank是20。因此需要增加bugrank.txt文件,内容如下:
16 Category PERFORMANCE
此时自定义规则生效。
二、findbugs与maven的集成
与maven的集成有两种形式,第一在pom文件build节点下面检查配置,第二在pom文件report节点下面配置findbugs插件,输出报告。
1、build节点配置findbugs插件
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>findbugs-maven-plugin</artifactId> <version>3.0.1</version> <executions> <execution> <goals> <goal>check</goal> </goals> <configuration> </configuration> </execution> </executions> </plugin>
这样当出现findbugs问题的时候会导致构建失败
2、reporting配置findbug插件
<plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>findbugs-maven-plugin</artifactId> <version>3.0.1</version> </plugin> </plugins>
这样运行mvn site的时候会生成findbugs插件
3、自定义规则的集成
maven的配置中除了提供把规则作为一个artifact的方式:
<execution> <goals> <goal>findbugs</goal> <goal>check</goal> </goals> <configuration> <!-- <pluginList>xxx</pluginList> --> <plugins> <plugin> <artifactId>com.ozl.findbugs</artifactId> <groupId>com.ozl</groupId> <version>1.0.0</version> </plugin> </plugins> </configuration> </execution>
这样规则的jar包可以通过maven管理起来了。
4、mojo代码的分析
MOJO的代码为FindBugsMojo,代码位于findbugs-maven-plugin-3.0.1.jar中,实际上是使用groovy实现的,把传入的参数构造为一个ant脚本,然后调用ant的能力拉去findbugs
def ant = new AntBuilder() log.info("Fork Value is ${fork}") if (log.isDebugEnabled()) { startTime = System.nanoTime() } def findbugsArgs = getFindbugsArgs(tempFile) ant.java(classname: "edu.umd.cs.findbugs.FindBugs2", inputstring: getFindbugsAuxClasspath(), fork: "${fork}", failonerror: "true", clonevm: "false", timeout: "${timeout}", maxmemory: "${maxHeap}m") { def effectiveEncoding = System.getProperty("file.encoding", "UTF-8") if (sourceEncoding) { effectiveEncoding = sourceEncoding } log.debug("File Encoding is " + effectiveEncoding) sysproperty(key: "file.encoding", value: effectiveEncoding) if (jvmArgs && fork) { log.debug("Adding JVM Args => ${jvmArgs}") String[] args = jvmArgs.split(FindBugsInfo.BLANK) args.each() { jvmArg -> log.debug("Adding JVM Arg => ${jvmArg}") jvmarg(value: jvmArg) } } if (debug || trace) { sysproperty(key: "findbugs.debug", value: true) } classpath() { pluginArtifacts.each() { pluginArtifact -> log.debug(" Adding to pluginArtifact ->" + pluginArtifact.file.toString()) pathelement(location: pluginArtifact.file) } } findbugsArgs.each { findbugsArg -> log.debug("Findbugs arg is ${findbugsArg}") arg(value: findbugsArg) } }
注意拉起的main方法就是:edu.umd.cs.findbugs.FindBugs2