SpotBugs
相关工具推荐
- 同语言(Java)质量检查工具:PMD | Checkstyle | Google-Java-Format
- 同语言(Java)格式化工具:Spotless
- 安全检查工具:CodeQL | Gosec(Go)
- 通用提交门禁框架:Pre-commit
1. 简介
SpotBugs 是 FindBugs 的官方继任者(FindBugs 已停止维护),是一款开源的 Java 静态代码分析工具。它通过分析编译后的 Java 字节码(.class 文件)来检测代码中超过 400 种已知的 Bug 模式,包括空指针异常、资源泄漏、线程安全问题、安全漏洞和不良编码实践等。
- 主要检查语言:Java(字节码分析)
- 主要检查能力:检测 Java 字节码中的空指针、并发问题、性能缺陷、不良实践等已知 Bug 模式;涵盖质量类检查(潜在 Bug 模式检测)
- 核心检查原理:字节码级分析,不依赖源代码
- 检查规则/选项:400+ 个标准 Bug Pattern(分布在 BAD_PRACTICE、CORRECTNESS、PERFORMANCE、SECURITY、STYLE 等 10 个大类),全量 Bug Pattern 列表
- GitHub 仓库:https://github.com/spotbugs/spotbugs(3,895 stars)
- 开源协议:LGPL-2.1-or-later
- 最新稳定版本:v4.9.3
- 运行环境要求:需 JRE 11+(v4.8+ 起);通过 Maven/Gradle 插件集成
- 误报率:中
2. 官方文档
3. 社区优秀实践
3.1 Apache 基金会项目
Apache 基金会多个顶级项目使用 SpotBugs 作为 Java 代码质量保障工具,通常在 pom.xml 中集成 spotbugs-maven-plugin 并配合自定义 Filter 文件。
- Apache Tomcat:https://github.com/apache/tomcat
- Apache Commons:https://github.com/apache/commons-lang
- Apache Maven:https://github.com/apache/maven
典型实践:在 verify 阶段自动执行 spotbugs:check,配合 exclude filter 排除自动生成代码和测试类。
3.2 Jenkins 社区
Jenkins 是 SpotBugs 的重要使用者,不仅自身代码使用 SpotBugs 检查,还通过 Jenkins SpotBugs Plugin 发布分析报告和趋势图。
- Jenkins 主仓库:https://github.com/jenkinsci/jenkins
- Jenkins SpotBugs Plugin:https://github.com/jenkinsci/spotbugs-plugin
典型实践:在 Jenkins Pipeline 中集成 SpotBugs 扫描,通过 SpotBugs Plugin 可视化展示分析结果,支持历史趋势追踪和构建失败阈值设置。
3.3 SonarQube / SonarSource
SonarQube 内置集成了 SpotBugs 作为 Java 代码的 Bug 检测引擎,在 SonarQube 的 Java 分析器中直接调用 SpotBugs 进行字节码分析。
- SonarQube 源码:https://github.com/SonarSource/sonarqube
- SonarQube Java Analyzer:https://github.com/SonarSource/sonar-java
典型实践:SonarQube 将 SpotBugs 的检测结果统一纳入其质量门禁(Quality Gate)体系,与其他规则(如自定义规则、PMD 规则)一起进行综合评估。
4. 工具配置说明
4.1 配置文件说明
SpotBugs 涉及以下配置文件:
| 配置文件 | 用途 | 使用场景 |
|---|---|---|
spotbugs-include.xml(Include Filter) |
包含过滤器,指定只检查匹配的类或 Bug 模式 | 需要聚焦特定包/类别/Bug 模式的项目 |
spotbugs-exclude.xml(Exclude Filter) |
排除过滤器,排除不需要检查的类或 Bug 模式 | 需要排除自动生成代码、测试类、特定 Bug 模式的项目 |
Effort 与 Threshold 配置(分析参数,可通过命令行或 Maven/Gradle 插件指定):
| 参数 | 可选值 | 说明 |
|---|---|---|
effort |
min |
最小分析深度,跳过部分分析,速度最快 |
default / more |
默认分析深度,平衡速度与检测量 | |
max |
最大分析深度,启用所有分析,检测最全面 | |
threshold |
high(对应 Confidence High) |
只报告高置信度的 Bug |
medium(对应 Confidence Medium) |
报告高和中置信度的 Bug(默认) | |
low(对应 Confidence Low) |
报告所有置信度的 Bug | |
exp |
报告实验性 Bug 模式 |
参考文档:Filter 文件完整说明、Effort 详细说明
4.2 Filter 配置详解
SpotBugs 的 Include Filter 与 Exclude Filter 均采用 FindBugsFilter XML 格式,通过 <Match> 及其子元素描述匹配条件。
配置项说明(Filter 匹配元素):
| 元素 | 类型 | 说明 | 示例 |
|---|---|---|---|
<Match> |
容器 | 匹配条件容器(子元素为 AND 关系) | - |
<Class> |
元素 | 按类名匹配(支持正则,~ 开头) |
<Class name="~com\.example\..*"/> |
<Package> |
元素 | 按包名匹配 | <Package name="com.example.service"/> |
<Source> |
元素 | 按源文件名匹配 | <Source name="~.*\.groovy"/> |
<Method> |
元素 | 按方法名/签名匹配 | <Method name="toString"/> |
<Field> |
元素 | 按字段名匹配 | <Field name="~.*password.*"/> |
<Bug pattern> |
元素 | 按 Bug 模式精确匹配 | <Bug pattern="NP_NULL_ON_SOME_PATH"/> |
<Bug code> |
元素 | 按 Bug 代码前缀匹配 | <Bug code="NP"/> |
<Bug category> |
元素 | 按 Bug 类别匹配 | <Bug category="SECURITY"/> |
<Confidence> |
元素 | 按置信度匹配(1=高,2=中,3=低) | <Confidence value="1"/> |
<Rank> |
元素 | 按 Bug 严重程度匹配(1-20) | <Rank value="9"/> |
<Or> |
容器 | 逻辑或 | 组合多个条件 |
<And> |
容器 | 逻辑与 | 组合多个条件 |
<Not> |
容器 | 逻辑非 | 取反条件 |
推荐配置示例:
Include Filter(spotbugs-include.xml,聚焦于高价值 Bug 模式,适合大多数 Java 项目):
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<!-- ===== 正确性(CORRECTNESS)===== -->
<Match>
<Bug pattern="NP_NULL_ON_SOME_PATH"/>
</Match>
<Match>
<Bug pattern="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"/>
</Match>
<Match>
<Bug pattern="NP_NULL_PARAM_DEREF"/>
</Match>
<!-- ===== 多线程正确性(MT_CORRECTNESS)===== -->
<Match>
<Bug pattern="LI_LAZY_INIT_STATIC"/>
</Match>
<Match>
<Bug pattern="DC_DOUBLECHECK"/>
</Match>
<Match>
<Bug pattern="IS2_INCONSISTENT_SYNC"/>
</Match>
<!-- ===== 安全(SECURITY)===== -->
<Match>
<Bug pattern="SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"/>
</Match>
<!-- ===== 不良实践(BAD_PRACTICE)===== -->
<Match>
<Bug pattern="OS_OPEN_STREAM"/>
</Match>
<Match>
<Bug pattern="OS_OPEN_STREAM_EXCEPTION_PATH"/>
</Match>
<Match>
<Bug pattern="RV_RETURN_VALUE_IGNORED"/>
</Match>
<Match>
<Bug pattern="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE"/>
</Match>
<Match>
<Bug pattern="HE_EQUALS_USE_HASHCODE"/>
</Match>
<Match>
<Bug pattern="HE_HASHCODE_NO_EQUALS"/>
</Match>
<Match>
<Bug pattern="EI_EXPOSE_REP"/>
</Match>
<Match>
<Bug pattern="EI_EXPOSE_REP2"/>
</Match>
<Match>
<Bug pattern="MS_EXPOSE_REP"/>
</Match>
<Match>
<Bug pattern="REC_CATCH_EXCEPTION"/>
</Match>
<Match>
<Bug pattern="DE_MIGHT_IGNORE"/>
</Match>
<Match>
<Bug pattern="ES_COMPARING_STRINGS_WITH_EQ"/>
</Match>
<!-- ===== 性能(PERFORMANCE)===== -->
<Match>
<Bug pattern="DM_DEFAULT_ENCODING"/>
</Match>
<Match>
<Bug pattern="DM_NUMBER_CTOR"/>
</Match>
</FindBugsFilter>
Exclude Filter(spotbugs-exclude.xml,排除自动生成代码、测试类、噪音类别):
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<!-- 排除自动生成的代码 -->
<Match>
<Class name="~.*\.generated\..*"/>
</Match>
<Match>
<Class name="~.*_generated"/>
</Match>
<!-- 排除测试类 -->
<Match>
<Class name="~.*Test"/>
</Match>
<Match>
<Class name="~.*IT$"/>
</Match>
<!-- 排除 Builder / DTO / Model / Entity 类 -->
<Match>
<Class name="~.*Builder"/>
</Match>
<Match>
<Class name="~.*DTO"/>
</Match>
<Match>
<Class name="~.*Model"/>
</Match>
<Match>
<Class name="~.*Entity"/>
</Match>
<!-- 排除噪音类别 -->
<Match>
<Bug category="NOISE"/>
</Match>
<!-- 排除实验性 Bug 模式 -->
<Match>
<Bug category="EXPERIMENTAL"/>
</Match>
</FindBugsFilter>
Maven/Gradle 插件中通过
includeFilterFile和excludeFilterFile参数引用上述过滤器文件,具体集成配置见第 5 章。
4.3 Bug 类别说明
SpotBugs 的 400+ Bug 模式分为以下类别:
| 类别 | 标识 | 说明 | 建议处理方式 |
|---|---|---|---|
| 恶意代码漏洞 | MALICIOUS_CODE |
可能被不受信任代码攻击的漏洞 | 必须修复 |
| 多线程正确性 | MT_CORRECTNESS |
线程、锁、volatile 相关缺陷 | 必须修复 |
| 安全 | SECURITY |
可能导致远程利用的安全漏洞 | 必须修复 |
| 正确性 | CORRECTNESS |
可能的编码错误 | 建议修复 |
| 不良实践 | BAD_PRACTICE |
违反推荐编码实践 | 建议修复 |
| 性能 | PERFORMANCE |
代码效率低下但不一定错误 | 视情况修复 |
| 国际化 | I18N |
国际化和区域设置相关缺陷 | 视需求修复 |
| 糟糕代码 | STYLE |
代码混乱、容易导致错误 | 可选修复 |
| 实验性 | EXPERIMENTAL |
实验性 Bug 模式 | 可选修复 |
完整 Bug 模式列表请参考:Bug descriptions
4.4 核心 Bug Detectors 列表
以下是 SpotBugs 最常用的检测器及其检测的 Bug 模式:
| 检测器 | Bug 模式 | 类别 | 说明 |
|---|---|---|---|
| FindNullDereference | NP_NULL_ON_SOME_PATH |
CORRECTNESS | 检测可能的空指针解引用 |
NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE |
CORRECTNESS | 方法返回值可能为 null,调用者未检查 | |
NP_NULL_PARAM_DEREF |
CORRECTNESS | 方法参数可能为 null,内部未检查 | |
| FindOpenStream | OS_OPEN_STREAM |
BAD_PRACTICE | 流资源未关闭 |
OS_OPEN_STREAM_EXCEPTION_PATH |
BAD_PRACTICE | 异常路径上流资源未关闭 | |
| FindReturnRef | EI_EXPOSE_REP |
MALICIOUS_CODE | 方法返回内部可变引用 |
EI_EXPOSE_REP2 |
MALICIOUS_CODE | 方法参数存储到字段,暴露内部状态 | |
MS_EXPOSE_REP |
MALICIOUS_CODE | public 方法返回可变内部字段 | |
| FindInconsistentSync | IS2_INCONSISTENT_SYNC |
MT_CORRECTNESS | 不一致的同步 |
| FindDoubleCheck | DC_DOUBLECHECK |
MT_CORRECTNESS | 双检锁模式不正确 |
| FindLazyInit | LI_LAZY_INIT_STATIC |
MT_CORRECTNESS | 静态字段非同步懒初始化 |
| FindSqlInjection | SQL_INJECTION_JDBC |
SECURITY | SQL 注入漏洞 |
| FindHEmismatch | HE_EQUALS_USE_HASHCODE |
BAD_PRACTICE | equals 方法使用了 hashCode |
HE_HASHCODE_NO_EQUALS |
BAD_PRACTICE | 定义了 hashCode 但未定义 equals | |
| DroppedException | DE_MIGHT_IGNORE |
BAD_PRACTICE | 可能忽略了异常 |
| DefaultEncodingDetector | DM_DEFAULT_ENCODING |
PERFORMANCE | 使用平台默认编码 |
| InefficientStringBuffering | SBSC_USE_STRINGBUFFER_CONSTRUCTOR |
PERFORMANCE | 低效的字符串拼接 |
| FindNonShortCircuit | NS_NON_SHORT_CIRCUIT |
BAD_PRACTICE | 使用 & 或 | 代替 && 或 || |
完整检测器列表请参考:Detectors
4.5 Confidence 级别说明
SpotBugs 的每个 Bug 报告都有一个 Confidence 级别,表示该检测的置信度:
| Confidence | 对应 threshold | 说明 |
|---|---|---|
| High | threshold=High |
高置信度,几乎确定是真实 Bug |
| Medium | threshold=Medium |
中置信度,很可能是 Bug,但有一定误报可能 |
| Low | threshold=Low |
低置信度,可能是 Bug,误报率较高 |
| Experimental | threshold=Exp |
实验性检测,仅供参考 |
推荐策略:
- CI/CD 流水线:使用
threshold=Medium,只阻断中高置信度的 Bug - 本地开发:使用
threshold=Low,尽可能多地发现问题 - 新项目首次引入:先用
threshold=High建立基线,逐步降低阈值
4.6 Bug 优先级(Rank)说明
SpotBugs 使用 1-20 的数值排名表示 Bug 的严重程度:
| 排名范围 | 优先级 | 说明 |
|---|---|---|
| 1-4 | Scariest(最高) | 最严重的 Bug,必须立即修复 |
| 5-9 | Scary(高) | 严重的 Bug,应尽快修复 |
| 10-14 | Troubling(中) | 中等严重,应在迭代内修复 |
| 15-20 | Of Concern(低) | 较低严重,可酌情处理 |
5. 主流集成方式
5.1 Maven 集成(生态主流,优先)
在 pom.xml 中添加 spotbugs-maven-plugin(最新版本 4.10.2.0):
<project>
<build>
<plugins>
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.10.2.0</version>
<dependencies>
<!-- 指定 SpotBugs 核心版本 -->
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs</artifactId>
<version>4.10.2</version>
</dependency>
</dependencies>
<configuration>
<effort>Max</effort>
<threshold>Low</threshold>
<xmlOutput>true</xmlOutput>
<htmlOutput>true</htmlOutput>
<failOnError>true</failOnError>
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
<includeFilterFile>spotbugs-include.xml</includeFilterFile>
<plugins>
<!-- 可选:添加 find-sec-bugs 安全检测插件 -->
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.14.0</version>
</plugin>
<!-- 可选:添加 sb-contrib 扩展检测器 -->
<plugin>
<groupId>com.mebigfatguy.sb-contrib</groupId>
<artifactId>sb-contrib</artifactId>
<version>7.7.4</version>
</plugin>
</plugins>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
常用 Maven 命令:
# 生成报告(不阻断构建)
mvn spotbugs:spotbugs
# 检查并阻断构建(发现 Bug 则构建失败)
mvn spotbugs:check
# 打开 GUI 查看结果
mvn spotbugs:gui
# 查看插件帮助
mvn spotbugs:help -Ddetail=true
参考文档:Maven 插件文档 | Maven 插件站点(完整参数)
增量检查:Maven 支持增量编译,只有变更的源文件才会被重新编译为 .class 文件。结合构建工具的增量编译能力,SpotBugs 可以只分析已编译的 .class 文件:
# Maven:增量编译后只分析变更的类
mvn compile spotbugs:check
通过 onlyAnalyze 参数限制分析范围到特定包或类,实现局部增量分析:
<configuration>
<onlyAnalyze>com.example.app.*</onlyAnalyze>
</configuration>
或通过命令行:
mvn spotbugs:check -Dspotbugs.onlyAnalyze="com.example.app.*"
5.2 pre-commit 集成
SpotBugs 需要编译后的 .class 文件,pre-commit hook 依赖本地 JDK 和构建工具链。
注意:以下配置使用
language: system,依赖本地已安装的 JDK 和 Maven/Gradle,执行环境需要预先完成项目编译。
repos:
- repo: local
hooks:
- id: spotbugs
name: SpotBugs Static Analysis
entry: mvn spotbugs:check -q -Dmaven.test.skip=true
language: system
files: \.java$
pass_filenames: false
安装钩子:
pip install pre-commit
pre-commit install
说明:
pass_filenames: false表示不将变更文件列表传给 hook,因为 SpotBugs 需要分析编译后的完整 class 文件。language: system要求本地环境已安装 JDK 和 Maven。
增量检查:SpotBugs 是字节码级分析工具,需要先编译 Java 源码生成 .class 文件才能分析,不适合放入 pre-commit 提交前检查。建议在本地手动运行 mvn compile spotbugs:check 或在 PR 门禁中执行。参见 Java 语言指南 中关于 SpotBugs 不放入 pre-commit 的说明。
5.3 IDE 集成
IntelliJ IDEA
JetBrains 官方维护了 SpotBugs IntelliJ 插件:
- 打开 File -> Settings -> Plugins
- 搜索 "SpotBugs" 并安装
- 安装后右键项目 -> SpotBugs -> Analyze Project Files
- 分析结果会在 SpotBugs Tool Window 中展示,支持按严重级别过滤
也可以通过 Maven/Gradle 插件间接使用:在 IDEA 的 Maven/Gradle 工具窗口中双击 spotbugs:check 任务即可运行。
Eclipse
- 打开 Help -> Eclipse Marketplace
- 搜索 "SpotBugs Eclipse Plugin" 并安装
- 右键项目 -> SpotBugs -> Find Bugs
- 分析结果会在 SpotBugs Explorer 视图中展示
可通过 m2e-code-quality 插件自动从 Maven 配置同步 SpotBugs 设置到 Eclipse。
VS Code
SpotBugs for VS Code 扩展支持在 VS Code 中直接运行 SpotBugs 分析:
- 在 VS Code 扩展市场搜索 "SpotBugs" 并安装
- 要求安装 "Language Support for Java by Red Hat" 扩展
- 使用命令面板运行 "SpotBugs: Analyze this workspace" 或 "SpotBugs: Analyze File/Folder"
- 分析结果在 "SpotBugs" 视图中展示,支持导出 SARIF 报告
5.4 命令行使用方式
下载安装
从 GitHub Releases 下载最新版本:
# 下载
wget https://github.com/spotbugs/spotbugs/releases/download/4.10.2/spotbugs-4.10.2.tgz
# 解压
tar -xzf spotbugs-4.10.2.tgz -C /opt/
# 配置环境变量
export SPOTBUGS_HOME=/opt/spotbugs-4.10.2
export PATH=$PATH:$SPOTBUGS_HOME/bin
基本命令
# 查看版本
spotbugs -version
# 文本模式分析 JAR 文件
spotbugs -textui -effort:max -high -medium -low \
-exclude spotbugs-exclude.xml \
./target/myapp.jar
# 生成 HTML 报告
spotbugs -textui -html -outputFile report.html \
-effort:max \
./target/classes
# 生成 XML 报告
spotbugs -textui -xml -outputFile report.xml \
./target/classes
# 生成 SARIF 报告(用于 GitHub Code Scanning)
spotbugs -textui -sarif -outputFile report.sarif.json \
./target/classes
# 启动 GUI 查看结果
spotbugs -gui ./target/classes
通过 JAR 直接运行
java -Xmx2048m -jar $SPOTBUGS_HOME/lib/spotbugs.jar \
-textui \
-effort:max \
-high -medium -low \
-exclude spotbugs-exclude.xml \
./target/myapp.jar
命令行参数说明
| 参数 | 说明 |
|---|---|
-textui |
命令行文本模式 |
-gui |
图形界面模式 |
-version |
显示版本号 |
-help |
显示帮助信息 |
-effort:min|default|more|max |
分析深度 |
-high / -medium / -low |
报告的优先级范围 |
-xml=path/to/file |
输出 XML 格式报告 |
-html=path/to/file |
输出 HTML 格式报告 |
-sarif=path/to/file |
输出 SARIF 格式报告 |
-exclude <filter.xml> |
排除过滤器文件 |
-include <filter.xml> |
包含过滤器文件 |
-onlyAnalyze <classes> |
限制分析范围到指定类/包 |
-maxHeap <size> |
JVM 最大堆内存(MB) |
-pluginList <jars> |
指定插件 JAR 列表 |
-visitors <v1,v2> |
只运行指定检测器 |
-omitVisitors <v1,v2> |
禁用指定检测器 |
-chooseVisitors <+v1,-v2> |
选择性启用/禁用检测器 |
-maxRank <rank> |
只报告优先级 <= 指定值的 Bug |
-bugCategories <cat1,cat2> |
只报告指定类别的 Bug |
参考文档:命令行选项
增量检查:通过 git diff 获取变更的 Java 文件列表,编译后只分析对应的 class 文件:
# 获取变更的 Java 文件并编译
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMR HEAD | grep '\.java$')
mvn compile
# 将变更的 .java 文件转换为 .class 文件路径,配合 onlyAnalyze 使用
# 注意:SpotBugs 的 onlyAnalyze 接受的是类全限定名而非文件路径
CLASSES=$(echo "$CHANGED_FILES" | sed 's|src/main/java/||;s|\.java$||;s|/|.|g')
spotbugs -textui -onlyAnalyze "$CLASSES" ./target/classes
CI 脚本调用:在 CI 环境中通过脚本调用 SpotBugs 进行静态分析。
GitHub Actions
name: SpotBugs Analysis
on: [push, pull_request]
jobs:
spotbugs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- name: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
- name: Run SpotBugs
run: mvn spotbugs:spotbugs
- name: Upload SpotBugs Report
uses: actions/upload-artifact@v4
if: always()
with:
name: spotbugs-report
path: target/spotbugs.html
GitLab CI
stages:
- test
spotbugs:
stage: test
image: maven:3.9-eclipse-temurin-17
script:
- mvn spotbugs:spotbugs
artifacts:
when: always
paths:
- target/spotbugs.html
- target/spotbugsXml.xml
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Jenkins Pipeline
pipeline {
agent any
tools {
maven 'Maven 3.9'
jdk 'JDK 17'
}
stages {
stage('SpotBugs') {
steps {
sh 'mvn spotbugs:spotbugs'
}
post {
always {
recordIssues(
tool: spotBugs(pattern: 'target/spotbugsXml.xml'),
qualityGates: [[threshold: 1, type: 'TOTAL', unstable: true]]
)
}
}
}
}
}
增量检查:GitHub Actions 中按 PR 变更文件触发增量检查:
name: Incremental SpotBugs
on:
pull_request:
paths:
- "src/main/java/**/*.java"
jobs:
spotbugs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
- name: Run SpotBugs
run: mvn spotbugs:check
GitLab CI 中按变更路径触发增量检查:
spotbugs:
stage: test
image: maven:3.9-eclipse-temurin-17
script:
- mvn spotbugs:check
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- src/main/java/**/*.java
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
5.5 Gradle 集成
使用官方 Gradle 插件(最新版本 6.5.6,默认集成 SpotBugs 4.10.2):
plugins {
id 'java'
id 'com.github.spotbugs' version '6.5.6'
}
spotbugs {
toolVersion = '4.10.2'
effort = 'max' // 分析深度:min | default | max
reportLevel = 'low' // 报告级别:high | medium | low(对应 confidence)
ignoreFailures = false
showProgress = true
excludeFilter = file('config/spotbugs/exclude.xml')
includeFilter = file('config/spotbugs/include.xml')
maxHeapSize = '1g'
}
// 可选:添加扩展插件
dependencies {
spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0'
spotbugsPlugins 'com.mebigfatguy.sb-contrib:sb-contrib:7.7.4'
}
spotbugsMain {
reports {
xml.required = true
html.required = true
html.stylesheet = 'fancy-hist.xsl'
}
}
Kotlin DSL 写法:
import com.github.spotbugs.snom.Confidence
import com.github.spotbugs.snom.Effort
spotbugs {
ignoreFailures = false
showProgress = true
effort = Effort.DEFAULT
reportLevel = Confidence.DEFAULT
excludeFilter = file("exclude.xml")
includeFilter = file("include.xml")
maxHeapSize = "1g"
}
常用 Gradle 命令:
# 检查主源码
./gradlew spotbugsMain
# 检查测试源码
./gradlew spotbugsTest
# 检查所有源码(./gradlew check 会自动触发)
./gradlew spotbugs
增量检查:Gradle 自动处理增量编译,只有变更的源文件才会被重新编译。结合 Gradle 的增量编译能力,SpotBugs 可以只分析已编译的 .class 文件:
# Gradle:增量编译 + SpotBugs 任务(Gradle 自动处理增量编译)
./gradlew spotbugsMain
通过 onlyAnalyze 配置限制分析范围到特定包或类:
spotbugs {
onlyAnalyze = ['com.example.app.*']
}
5.6 Ant 集成
SpotBugs 提供原生 Ant 任务支持:
<taskdef
name="spotbugs"
classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
classpath="${spotbugs.home}/lib/spotbugs-ant.jar"/>
<target name="spotbugs" depends="compile">
<spotbugs
home="${spotbugs.home}"
output="html"
outputFile="${basedir}/spotbugs-report.html"
effort="max"
threshold="low"
excludeFilter="${basedir}/spotbugs-exclude.xml">
<class location="${basedir}/build/classes"/>
<auxClasspath path="${basedir}/lib"/>
<sourcePath path="${basedir}/src/main/java"/>
</spotbugs>
</target>
参考文档:Ant 任务文档
6. 告警抑制(屏蔽)方法
6.1 通过代码注解屏蔽:@SuppressFBWarnings
在 Java 代码中使用 @SuppressFBWarnings 注解抑制特定告警,需要添加 spotbugs-annotations 依赖:
Maven 依赖:
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>4.10.2</version>
<optional>true</optional>
</dependency>
Gradle 依赖:
compileOnly 'com.github.spotbugs:spotbugs-annotations:4.10.2'
使用方式:
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
// 方法级抑制
@SuppressFBWarnings(
value = "DM_DEFAULT_ENCODING",
justification = "Output is for local terminal display only, UTF-8 is guaranteed"
)
public void printOutput(String text) {
System.out.println(text);
}
// 字段级抑制
@SuppressFBWarnings("URF_UNREAD_FIELD")
private int debugCounter;
// 类级抑制(抑制多种 Bug 模式)
@SuppressFBWarnings({
"EI_EXPOSE_REP2",
"MS_EXPOSE_REP"
})
public class Configuration {
private Date configDate;
// ...
}
// 参数级抑制
public void process(@SuppressFBWarnings("NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE") String input) {
// ...
}
注意:SpotBugs 不能使用 Java 标准的
@SuppressWarnings注解,因为它是源代码级注解(RetentionPolicy.SOURCE),在字节码中不可见。必须使用 SpotBugs 专用的@SuppressFBWarnings(RetentionPolicy.CLASS)。
6.2 通过 Filter 文件屏蔽(项目级)
通过 XML 过滤器文件全局排除特定告警,适用于无法修改源代码的场景(如第三方库、自动生成的代码):
<?xml version="1.0" encoding="UTF-8"?>
<FindBugsFilter>
<!-- 排除特定 Bug 模式 -->
<Match>
<Bug pattern="DM_DEFAULT_ENCODING"/>
</Match>
<!-- 排除特定包中的所有 Bug -->
<Match>
<Package name="com.example.generated"/>
</Match>
<!-- 排除特定类中的特定 Bug -->
<Match>
<Class name="com.example.legacy.LegacyService"/>
<Bug pattern="REC_CATCH_EXCEPTION"/>
</Match>
<!-- 排除 Groovy 生成代码 -->
<Match>
<Source name="~.*\.groovy"/>
</Match>
</FindBugsFilter>
Maven 中引用:
<configuration>
<excludeFilterFile>spotbugs-exclude.xml</excludeFilterFile>
</configuration>
Gradle 中引用:
spotbugs {
excludeFilter = file('spotbugs-exclude.xml')
}
命令行引用:
spotbugs -textui -exclude spotbugs-exclude.xml ./target/classes
参考文档:Filter 文件说明 | Filter 文件示例
6.3 通过 Maven/Gradle 配置屏蔽
Maven 排除特定类
通过 onlyAnalyze 参数限制分析范围,间接排除不需要分析的类:
<configuration>
<onlyAnalyze>com.example.app.*,-com.example.app.generated.*</onlyAnalyze>
</configuration>
Maven excludeBugsFile(按 Bug 实例排除)
excludeBugsFile 用于排除特定的 Bug 实例(基于 Bug Instance 的唯一标识):
<configuration>
<excludeBugsFile>spotbugs-exclude-bugs.xml</excludeBugsFile>
</configuration>
Gradle 配置屏蔽
spotbugs {
// 限制分析范围
onlyAnalyze = ['com.example.app.*']
// 排除过滤器
excludeFilter = file('exclude.xml')
// 包含过滤器
includeFilter = file('include.xml')
// 基线文件(排除已知的 Bug)
baselineFile = file('baseline.xml')
}
参考文档:Maven 插件参数 | Gradle 插件扩展配置
6.4 通过命令行参数屏蔽
# 排除特定检测器
spotbugs -textui -omitVisitors FindNonShortCircuit,FindNullDereferences ./target/classes
# 选择性启用/禁用检测器
spotbugs -textui -chooseVisitors "-FindNonShortCircuit,+TestASM" ./target/classes
# 只报告特定类别的 Bug
spotbugs -textui -bugCategories CORRECTNESS,SECURITY ./target/classes
# 只报告高优先级 Bug(maxRank)
spotbugs -textui -maxRank 9 ./target/classes
# 排除基线中已存在的 Bug
spotbugs -textui -excludeBugs baseline.xml ./target/classes
参考文档:命令行选项 - 输出过滤选项 | 命令行选项 - 检测器配置选项
6.5 通过 @Nonnull / @CheckForNull 注解辅助减少误报
通过添加空值注解帮助 SpotBugs 更精确地理解代码意图,减少误报:
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.CheckReturnValue;
public class UserService {
// 标记返回值不为 null
@NonNull
public User findUserById(long id) {
return userRepository.findById(id);
}
// 标记返回值可能为 null
@CheckForNull
public User findOptionalUser(long id) {
return userRepository.findByIdOrNull(id);
}
// 标记返回值必须被使用
@CheckReturnValue
public boolean validate(String input) {
return input != null && !input.isEmpty();
}
}
参考文档:Annotations 文档
6.6 告警抑制方法总结
| 方法 | 粒度 | 适用场景 | 是否需要修改代码 | 官方文档 |
|---|---|---|---|---|
@SuppressFBWarnings |
方法/字段/类/参数 | 已确认的误报或可接受的例外 | 是 | Annotations |
| Exclude Filter 文件 | 项目/包/类/Bug 模式 | 第三方代码、生成代码、批量排除 | 否 | Filter file |
| Exclude Bugs File | Bug 实例 | 排除特定已知 Bug 实例 | 否 | Maven 插件参数 |
@Nonnull/@CheckForNull |
方法/参数/字段 | 辅助减少空指针误报 | 是 | Annotations |
-omitVisitors |
检测器级别 | 禁用特定检测器 | 否 | 命令行选项 |
-maxRank / threshold |
优先级级别 | 屏蔽低优先级告警 | 否 | 命令行选项 |
Maven onlyAnalyze |
包/类级别 | 限制分析范围 | 否 | Maven 插件参数 |
Gradle baselineFile |
Bug 实例 | 排除基线中已存在的 Bug | 否 | Gradle 扩展配置 |