本文转载自InfoQ中文站,作者:Johan Janssen,译者:BO,策动:丁晓昀,审校:马可薇
Error Prone是谷歌开源的一个 Java 编译插件,能够在编译时进行动态剖析、bug 检测,或者对可能的优化提出倡议。插件中包含了超过 500 个预约义的bug查看,并且容许第三方和自定义插件。查看到问题之后,Error Prone 可能将问题通过 warning 显示进去或者用预约义的解决方案主动批改代码。Error Prone 反对 Java 8、11,以及 17,能够被用来修复 bug 或者大规模重构。文档中提供了应用 Maven、Bazel、Ant 以及 Grandle 的装置和配置教程。须要将 Error Prone 在编译器中配置为 annotation processor(注解处理器),上面是通过 Maven 创立测试工程的示例:

<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-compiler-plugin</artifactId>    <version>3.10.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.15.0</version>            </path>        </annotationProcessorPaths>    </configuration></plugin>

接下来能够创立一个示例类。上面的办法应用了 equals 办法来比照两个数组,更精确地说,此处所比拟的是对象自身而不是数组的内容。

public boolean compare(String firstList[], String secondList[]) {    return firstList.equals(secondList);}

执行 mvn clean verify 触发 Error Prone 剖析,上面是运行后果中的错误信息中:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:        compile (default-compile) on project ErrorProne: Compilation failure[ERROR] …/ErrorProne/src/main/java/org/example/Main.java:[5,28]     [ArrayEquals] Reference equality used to compare arrays[ERROR]   (see https://errorprone.info/bugpattern/ArrayEquals)[ERROR]   Did you mean 'return Arrays.equals(firstList, secondList);'?

报出了ArrayEquals谬误,Error Prone 的倡议是批改实现形式,以比拟数组的内容而不是比拟对象。

return Arrays.equals(firstList, secondList);

报错不仅能够帮忙改善代码,也能够让 Error Prone 主动利用解决方案。 -XepPatchChecks 参数的利用应该蕴含由逗号分隔开的 bug 模式列表,在下面的状况中,只有 ArrayEquals 解决方案用于这段代码。 -XepPatchLocation 参数用于具体定位解决方案文件地位,在以后情境中是批改了源文件:

<compilerArgs>    <arg>-XDcompilePolicy=simple</arg>    <arg>-Xplugin:ErrorProne -XepPatchChecks:ArrayEquals            -XepPatchLocation:IN_PLACE</arg></compilerArgs>

当初,在执行 mvn clean verify 之后,类文件被主动批改为:

public boolean compare(String firstList[], String secondList[]) {    return Arrays.equals(firstList, secondList);}

文档里提供了更多对于命令行标识的信息。除了内置的 bug 模式,也能够应用例如SLF4J等第三方公布的插件,或创立自定义插件。内置规定的源码提供了多种可用于定义插件的不同示例模板。例如,自定义一个可能用新的 JUnit 5 @BeforeEach 注解器代替旧版 @Before JUnit 注解器的 Error Prone 插件。

和前文例子不同,自定义的 Error Prone 插件应该被搁置于 Maven 模块。Error Prone 通过服务加载器机制来加载 bug 检测。这类之际通常肯定的配置,然而谷歌的AutoService我的项目借助 @AutoService 注解简化了配置工作。@BugPattern 注解用于定义 bug 的名称、简介以及严重性。在上面的例子中,如果没有找到 @Before 注解器会返回Description.NO_MATCH ,否则SuggestedFix会用 @BeforeEach 注解代替 @Before 注解。

@AutoService(BugChecker.class)@BugPattern(    name = "BeforeCheck",    summary = "JUnit 4's @Before is replaced by JUnit 5's @BeforeEach",    severity = BugPattern.SeverityLevel.SUGGESTION)public class BeforeCheck extends BugChecker implements BugChecker.AnnotationTreeMatcher {    private static final Matcher<AnnotationTree> matcher =            isType("org.junit.Before");    @Override    public Description matchAnnotation(AnnotationTree annotationTree,             VisitorState visitorState) {        if (!matcher.matches(annotationTree, visitorState)) {            return Description.NO_MATCH;        }        return describeMatch(annotationTree,             SuggestedFix.replace(annotationTree, "@BeforeEach"));    }}

构建自定义 Error Prone 插件的时候都是须要 Error Prone 和 AutoService 依赖的。

<dependency>  <groupId>com.google.errorprone</groupId>  <artifactId>error_prone_annotations</artifactId>  <version>2.15.0</version></dependency><dependency>  <groupId>com.google.errorprone</groupId>  <artifactId>error_prone_check_api</artifactId>  <version>2.15.0</version></dependency><dependency>  <groupId>com.google.auto.service</groupId>  <artifactId>auto-service-annotations</artifactId>  <version>1.0.1</version></dependency>

AutoService 应该被配置为一个注解处理器。

<annotationProcessorPaths>    <path>        <groupId>com.google.auto.service</groupId>        <artifactId>auto-service</artifactId>        <version>1.0.1</version>    </path></annotationProcessorPaths>

当初,自定义的 Error Prone 插件能够通过 mvn install 命令,装置在本地的 Maven 仓库。执行命令后,示例工程应该会被配置为应用新的自定义插件作为注解处理器。

<annotationProcessorPaths>    <path>        <groupId>org.example.custom.plugin</groupId>        <artifactId>ErrorProneBeforeCheck</artifactId>        <version>1.0-SNAPSHOT</version>    </path></annotationProcessorPaths>新的 BeforeCheck 应该被退出到了 Error Prone 剖析中。<compilerArgs>  <arg>-XDcompilePolicy=simple</arg>  <arg>-Xplugin:ErrorProne -XepPatchChecks:BeforeCheck            -XepPatchLocation:IN_PLACE</arg></compilerArgs>

增加一个示例测试类,其中蕴含@Before和@BeforeEach的两个注解。

public class ErrorProneTest {  @Before  void before() {  }  @BeforeEach  void beforeEach() {  }}

运行 mvn verify 时,新的自定义 Error Prone 插件将用@BeforeEach注解替换@Before注解。

public class ErrorProneTest {  @BeforeEach  void before() {  }  @BeforeEach  void beforeEach() {  }}

Error Prone 所应用的 Java internal 目前处于暗藏状态,可能会导致如下谬误:

java.lang.IllegalAccessError: class com.google.errorprone.BaseErrorProneJavaCompiler (in unnamed module @0x1a6cf771) cannot access class com.sun.tools.javac.api.BasicJavacTask (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.api to unnamed module @0x1a6cf771

Maven 的解决办法是通过在我的项目根目录下创立.mvn 目录来裸露 Java internal,在目录中创立一个 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

或者能够将--add-exports和--add-opens 参数配置增加到 Maven 编译器插件的 pom 文件中:

<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-compiler-plugin</artifactId>    <version>3.10.1</version>    <configuration>        <compilerArgs>            <arg>--add-exports</arg>            <arg>jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>          …

更多在 Bazel、Ant 和 Gradle 中应用 Error Prone 的信息可参见装置疏导。