1. 引言

代码质量是应用成功部署的关键因素。Bug和错误会严重影响软件的功能性和稳定性。Error Prone正是解决这类问题的利器——这是一个由Google内部维护并使用的库,能帮助Java开发者在编译阶段就发现并修复常见编程错误。

本文将深入探讨Error Prone库的核心功能,从安装配置到自定义规则,全面展示它如何提升代码质量和健壮性。

2. 安装配置

Error Prone已发布在Maven中央仓库。我们需要修改编译器配置来启用检查:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.12.1</version>
            <configuration>
                <release>17</release>
                <encoding>UTF-8</encoding>
                <compilerArgs>
                    <arg>-XDcompilePolicy=simple</arg>
                    <arg>-Xplugin:ErrorProne</arg>
                </compilerArgs>
                <annotationProcessorPaths>
                    <path>
                        <groupId>com.google.errorprone</groupId>
                        <artifactId>error_prone_core</artifactId>
                        <version>2.23.0</version>
                    </path>
                </annotationProcessorPaths>
            </configuration>
        </plugin>
    </plugins>
</build>

⚠️ JDK 16+的兼容性问题:由于JDK 16引入了强封装内部API,需添加额外JVM参数。在项目根目录创建.mvn/jvm.config文件:

--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED
--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED

若使用外部编译器或启用了maven-toolchains-plugin,需将参数移至compilerArgs

<compilerArgs>
    <!-- 其他参数 -->
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
    <arg>-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED</arg>
    <!-- 其他export/open参数 -->
</compilerArgs>

3. Bug模式识别

理解常见Bug模式是保障软件稳定性的基础。Error Prone通过模式识别在开发阶段主动预防问题。

3.1 预定义Bug模式

**插件内置500+种预定义Bug模式**。以DeadException为例:

public static void main(String[] args) {
    if (args.length == 0 || args[0] != null) {
        new IllegalArgumentException(); // 踩坑:创建了异常但未抛出
    }
    // 使用args[0]的其他操作
}

这段代码本想抛出异常,却因疏忽只创建了异常对象。执行mvn clean verify后,Error Prone会报错:

[ERROR] /path/to/Main.java:[6,12] [DeadException] Exception created but not thrown
    (see https://errorprone.info/bugpattern/DeadException)
  Did you mean 'throw new IllegalArgumentException();'?

智能提示:插件不仅发现问题,还直接给出修复建议。

3.2 自定义Bug模式

Error Prone支持创建自定义检查器,可针对特定代码库定制规则。

步骤1:创建检查器项目

新建项目my-bugchecker-plugin,配置依赖:

<dependencies>
    <dependency>
        <groupId>com.google.errorprone</groupId>
        <artifactId>error_prone_annotation</artifactId>
        <version>2.23.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.errorprone</groupId>
        <artifactId>error_prone_check_api</artifactId>
        <version>2.23.0</version>
    </dependency>
    <dependency>
        <groupId>com.google.auto.service</groupId>
        <artifactId>auto-service-annotations</artifactId>
        <version>1.0.1</version>
    </dependency>
</dependencies>

步骤2:实现检查器

创建检查空方法的规则:

@AutoService(BugChecker.class)
@BugPattern(
    name = "EmptyMethodCheck", 
    summary = "Empty methods should be deleted", 
    severity = BugPattern.SeverityLevel.ERROR
)
public class EmptyMethodChecker extends BugChecker implements BugChecker.MethodTreeMatcher {

    @Override
    public Description matchMethod(MethodTree methodTree, VisitorState state) {
        if (methodTree.getBody().getStatements().isEmpty()) {
            return describeMatch(methodTree, SuggestedFix.delete(methodTree));
        }
        return Description.NO_MATCH;
    }
}

步骤3:集成到主项目

  1. 打包检查器:mvn clean install
  2. 在主项目中添加依赖:
<annotationProcessorPaths>
    <path>
        <groupId>com.google.errorprone</groupId>
        <artifactId>error_prone_core</artifactId>
        <version>2.23.0</version>
    </path>
    <path>
        <groupId>com.example</groupId>
        <artifactId>my-bugchecker-plugin</artifactId>
        <version>1.0-SNAPSHOT</version>
    </path>
</annotationProcessorPaths>

当遇到空方法时:

public class ClassWithEmptyMethod {
    public void theEmptyMethod() {} // 触发检查
}

编译报错:

[EmptyMethodCheck] Empty methods should be deleted

4. 检查规则定制

虽然Error Prone很强大,但有时需要灵活调整严格程度。

4.1 局部抑制警告

使用@SuppressWarnings忽略特定检查:

@SuppressWarnings("EmptyMethodCheck")
public void emptyMethod() {} // 仅此方法忽略检查

⚠️ 谨慎使用:仅在与外部库集成等特殊场景下使用。

4.2 全局控制检查级别

通过编译参数调整检查严格度:

参数 效果
-Xep:CheckName 使用注解定义的级别
-Xep:CheckName:OFF 禁用检查
-Xep:CheckName:WARN 降级为警告
-Xep:CheckName:ERROR 升级为错误

全局控制参数:

-XepAllErrorsAsWarnings      # 所有错误转警告
-XepAllSuggestionsAsWarnings # 所有建议转警告
-XepDisableAllChecks         # 禁用所有检查
-XepDisableAllWarnings       # 禁用所有警告

组合使用示例:

<compilerArgs>
    <arg>-Xplugin:ErrorProne -XepDisableAllChecks -Xep:EmptyMethodCheck:ERROR</arg>
</compilerArgs>

效果:仅启用自定义的EmptyMethodCheck检查。

5. 代码重构能力

Error Prone的独特优势:不仅能发现问题,还能自动修复代码。

5.1 智能修复建议

当检测到DeadException时,它会建议:

Did you mean 'throw new IllegalArgumentException();'?

5.2 自动化代码修复

启用修复功能需添加参数:

  • -XepPatchChecks: 指定要修复的检查项
  • -XepPatchLocation: 修复位置(IN_PLACE直接修改源码)

配置示例:

<compilerArgs>
    <arg>-Xplugin:ErrorProne -XepPatchChecks:DeadException,EmptyMethodCheck -XepPatchLocation:IN_PLACE</arg>
</compilerArgs>

对问题代码:

public class BuggyClass {
    public static void main(String[] args) {
        if (args.length == 0 || args[0] != null) {
            new IllegalArgumentException(); // 未抛出
        }
    }
    public void emptyMethod() {} // 空方法
}

执行编译后自动重构为:

public class BuggyClass {
    public static void main(String[] args) {
        if (args.length == 0 || args[0] != null) {
            throw new IllegalArgumentException(); // 自动添加throw
        }
    }
    // 空方法被自动删除
}

6. 总结

Error Prone通过三大核心能力提升开发效率:

  1. 精准错误检测:500+预定义规则覆盖常见陷阱
  2. 灵活规则定制:支持自定义检查器和级别控制
  3. 自动化重构:编译时直接修复代码问题

对于追求代码质量的团队,Error Prone是不可或缺的工具。它将静态分析融入编译流程,用简单粗暴的方式消灭潜在Bug,让开发者更专注于业务逻辑。

完整示例代码见GitHub仓库


原始标题:Catch Common Mistakes with Error Prone Library in Java | Baeldung