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