共计 5630 个字符,预计需要花费 15 分钟才能阅读完成。
本文转载自 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 的信息可参见装置疏导。