写点什么

Error Prone 通过检测常见错误帮助改善 Java 代码

  • 2022-10-26
    北京
  • 本文字数:3718 字

    阅读完需:约 12 分钟

Error Prone 通过检测常见错误帮助改善Java代码

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 的信息可参见安装引导


相关阅读:

Java 近期新闻:顺序集合、Spring 6.0-RC1、Tomcat、Reactor 2022.0-RC1

Spring Boot 3 将于 2022 年 11 月发布,延迟了对 Java 模块系统的支持


2022-10-26 08:009122

评论

发布
暂无评论
发现更多内容

HDC.Cloud Day | 全国首场上海站告捷,聚开发者力量造梦、探梦、筑梦

华为云开发者联盟

云计算 华为云

1000道最新高频Java面试题,覆盖25个技术栈(多线程、JVM、高并发、spring、微服务、kafka,redis、分布式)从底层原理到架构!

程序知音

Java Java 面试 大厂面试 java架构 后端技术

PCB焊接出问题了,是PCB工程师的锅吗?

华秋PCB

PCB PCB设计 焊接

我服了,阿里挖过来的leader连垃圾回收都说不清楚

钟奕礼

Java java程序员 java面试 java编程

聊聊mybatis的反射之Reflector类

急需上岸的小谢

11月月更

一个 3 年 Java 程序员 5 家大厂的面试总结(已拿Offer)

钟奕礼

Java java程序员 java面试 java编程

被性能优化撂倒无数次后的顿悟!465页调优笔记助力大厂面试之旅

程序知音

Java JVM 性能调优 java架构 后端技术

一个关于X证券20000台服务器的血泪故事

青藤云安全

网络安全 青藤云安全

阿里P8大佬神创“Netty突击笔记”,堪称全网最全最牛逼的核心原理手册

程序知音

Java Netty JAVA开发 java架构 后端技术

首份关基安全国标刚发布,客户把我叫到了办公室......

青藤云安全

网络安全 青藤云安全

三个经典的MySQL问题

千锋IT教育

Python3.10的开发环境的搭建

千锋IT教育

Serverless 的前世今生

阿里巴巴云原生

阿里云 Serverless 云原生

聊聊mybatis的架构模块

急需上岸的小谢

11月月更

PG SQL 语法汇总

蜗牛也是牛

【Go】力扣 - 剑指 Offer 第五天 - 二维数组中的查找

陈明勇

Go 数据结构与算法 力扣 11月月更

微服务平滑迁移上云最佳实践

阿里巴巴云原生

阿里云 微服务 云原生

行业首个测试开发技术大赛开始报名啦~ 10万现金奖励等你来挑战

测吧(北京)科技有限公司

软件测试

聊聊索引

急需上岸的小谢

11月月更

Docker搭建私有registry镜像仓库

蜗牛也是牛

数据库索引相关和EFCore的索引映射

C++后台开发

数据库 后端开发 Linux服务器开发 C++开发 数据库索引

开源|ftlog升级啦!功能更强,性能更高,细节更极致

非凸科技

防止会议被入侵,华为云会议更专业

IT科技苏辞

聊一聊华为云弹性公网IP的那些事儿

爱尚科技

索引数据结构千千万 , 为什么B+Tree独领风骚

程序知音

小令观点 | 急需身份证扫描件?【A4证照扫描王】来帮你

令牌云数字身份

软件推荐 入职 打印 证照扫描

简述SpringAOP的实现原理

千锋IT教育

Wallys//IPQ6010/IPQ6018/IPQ6000/industrial wifi6 router/DFS/M.2/ 2x2 2.4GHz/5GHz

wallysSK

IPQ6010 ipq6018 IPQ6000

Java面试读这一篇就够了:100个互联网大厂Java面试真题整理

钟奕礼

Java Java 面试 java程序员 java编程

进大厂必刷的Java面试题

钟奕礼

Java java程序员 java面试 java编程

基于云原生网关的可观测性最佳实践

阿里巴巴云原生

阿里云 微服务 云原生 可观测

Error Prone 通过检测常见错误帮助改善Java代码_编程语言_Johan Janssen_InfoQ精选文章