提高代码质量的方法有很多,为此构建的工具也很多。在Java技术栈中,SpotBugs就是一个常用的工具。

1 SpotBugs是什么

SpotBugs是一个静态分析Java代码错误的工具,它是从FindBugs分出来的。目前SpotBugs可检查出400多种错误模式(具体说明在这里)。

SpotBugs可以分析任何Java版本编译的程序,但是运行它需要JDK 11或以上版本才可以。

2 SpotBugs的使用方法

SpotBugs既可以独立运行,也可以作为插件结合其他框架/工具一起使用。最为常见的是Maven插件、Gradle插件、Eclipse插件、IntelliJ插件。

下面仅以Maven插件与Eclipse插件来说明使用方法。

2.1 Maven插件

要在Maven工程中使用SpotBugs插件,首先需要在工程pom.xml文件中增加spotbugs-maven-plugin。

<plugin>
  <groupId>com.github.spotbugs</groupId>
  <artifactId>spotbugs-maven-plugin</artifactId>
  <version>4.9.3.0</version>
  <dependencies>
    <!-- overwrite dependency on spotbugs if you want to specify the version of spotbugs -->
    <dependency>
      <groupId>com.github.spotbugs</groupId>
      <artifactId>spotbugs</artifactId>
      <version>4.9.3</version>
    </dependency>
  </dependencies>
</plugin>

这个插件最常用的goal(目标/任务)有3个,分别是:spotbugs、check、gui。

2.1.1 spotbugs

执行spotbugs的命令如下:

mvn spotbugs:spotbugs

输出结果类似下图:

goal-spotbugs

spotbugs会尝试静态分析当前Maven工程的代码质量问题(使用详情见这里),并将分析结果输出到工程target目录内。分析结果包含两部分:一个spotbugsXml.xml文件;一个site目录。具体如下图:

goal-spotbugs-result

2.1.2 check

执行check的命令如下:

mvn spotbugs:check

输出结果类似下图:

goal-check

check会像spotbugs一样尝试静态分析当前Maven工程的代码质量问题,但在发现任何错误时会抛出错误并立即停止构建工程(使用详情见这里)。

2.1.3 gui

执行gui的命令如下:

mvn spotbugs:gui

输出结果类似下图:

goal-gui

gui的主要作用是会启动SpotBugs的GUI界面,分析之前spotbugs产生的结果内容(使用详情见这里)。SpotBugs的GUI界面如下图所示:

goal-gui-result

2.2 Eclipse插件

安装SoptBugs的Eclipse插件,需要打开Eclipse的Eclipse Marketplace,然后搜索“SpotBugs”。具体如下图:

spotbugs-eclipse

点击“安装”即可完成SpotBugs的Eclipse插件的安装过程。

使用方法也很简单,只要在想要使用SpotBugs扫描的工程上右键,选择“SpotBugs” - “Find Bugs”,即可开始做代码静态分析。具体如下图:

fire-spotbugs-eclipse

SpotBugs执行完成后,可以在工具栏打开“Bug Explorer”来查看结果。双击具体的Bug记录,即可定位到有问题的源码。例如下面这个Bug:

fire-spotbugs-eclipse-result

这是一个Null Pointer Exception(空指针异常)。当switch的判断走默认case时,因为mv是null(未初始化),所以调用mv.setViewName方法会失败。

2.3 IntelliJ插件

使用详见这里。(我不使用IntelliJ,所以就不详细介绍啦~)

3 SpotBugs可识别的Bug模式

目前SpotBugs可识别超过400种Bug模式,具体描述在这里。SpotBugs将它们分成了10个大类:

类型 描述
Bad practice (BAD_PRACTICE) 这类问题主要是因为坏的编码习惯造成的。它们都违反了基本的编码实践。例如hashCode()和equals()问题、Serializable问题以及finalize()使用问题等。
Correctness (CORRECTNESS) 可能导致错误的代码。这些代码的实现明显与开发人员的意图不符。例如一个父类方法加了@OverridingMethodsMustInvokeSuper注解,但是子类overriding的方法中没有调用super方法。
Experimental (EXPERIMENTAL) SpotBugs试验性的或未经充分验证的错误。
Internationalization (I18N) 与国际化和本地化有关的代码缺陷。
Malicious code vulnerability (MALICIOUS_CODE) 易受不信任代码攻击的bug。例如finalizer应该是protected的,而不是public的
Multithreaded correctness (MT_CORRECTNESS) 与线程、锁和内存可见性有关的bug。例如在多线程环境下对资源进行非线程安全的操作。
Bogus random noise (NOISE) 主要是作为数据挖掘实验中的对照,而非用于查找软件中的实际bug。
Performance (PERFORMANCE) 可能存在性能问题的代码,不一定是bug。例如在不同的java文件中重复出现多次完全相同的、超大尺寸的字符串。
Security (SECURITY) 对输入数据未做任何安全处理,从而可能产生被远程利用的安全漏洞bug。例如可能造成XSS攻击的问题。
Dodgy code (STYLE) 代码混乱、反常或编写方式会导致错误等。例如从未使用的变量、无效的switch流程、未确认的强制类型转换以及对确定为空的值进行多余的空检查等。

4 总结

SpotBugs无法识别出所有的代码bug,也无法保证所有识别出的bug都是准确的。但它确实大大提高了Java代码质量改进的效率,降低了改进成本。我们应该把SpotBugs作为优化代码的第一步操作。