使用 OpenRewrite 优化代码

使用 OpenRewrite 优化代码

OpenJDK 21 升级指南 中提到, OpenRewrite 可以帮忙解决一些升级 OpenJDK 中发现的问题。随着不断的探索,D瓜哥发现,OpenRewrite 的功能远远不止这些。下面就挑选一些重要的功能来给大家做一些讲解。

为了方便查看改动点,建议将代码交给版本管理工具,比如 Git,来管理。

快速入门

OpenRewrite 是一套对源码做重构的大型生态系统,可以帮助开发人员减少技术债。所以,它提供了一套的相关工具。对于大多数开发人员来说,最方便的也许就是基于 Maven 插件的相关工具。这里以对 Java 的 import 语句排序来为示例展示一下 OpenRewrite 的使用方法。

在项目的 pom.xml 中增加如下配置:

<!-- @author: D瓜哥 · https://www.diguage.com -->
<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>5.30.0</version>
  <configuration>
    <activeRecipes>
      <!-- import 排序 -->
      <!-- https://docs.openrewrite.org/recipes/java/orderimports -->
      <recipe>org.openrewrite.java.OrderImports</recipe>
    </activeRecipes>
  </configuration>
</plugin>

然后执行如下命令:

mvn rewrite:run

执行会输出一大堆东西,这里就不再展示,执行完成后,使用 Git 查看一下改动点。如下图:

使用 OpenRewrite 排序 import 的改动点
图 1. 使用 OpenRewrite 排序 import 的改动点

将这些修改点提交,就完成了一次优化, OpenRewrite 的基本使用,你学废了吗?

这里再多说一句: 由于 OpenRewrite 精巧的设计,可以通过使用不同的处方,进行各种各样的优化。所以,最重要的一点就是了解 OpenRewrite 各种不同的处方及使用办法。下面就介绍一下常用的处方及使用办法。

常用处方

升级到 Java 21

OpenJDK 21 升级指南 中提到,可以使用“科技与狠活”来解决很多升级中遇到的问题。这里就来实操一把。

OpenRewrite 提供了升级到 Java 17 和 Java 21 的相关处方。这里直接演示升级到 Java 21 的使用。另外, OpenRewrite 的很多处方没有包含在核心库里,而是提供了单独的库。所以,整体配置如下:

<!-- @author: D瓜哥 · https://www.diguage.com -->
<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>5.30.0</version>
  <configuration>
    <activeRecipes>
      <!-- 升级到 Java 21 -->
      <!-- https://docs.openrewrite.org/recipes/java/migrate/upgradetojava21 -->
      <recipe>org.openrewrite.java.migrate.UpgradeToJava21</recipe>
    </activeRecipes>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>org.openrewrite.recipe</groupId>
      <artifactId>rewrite-migrate-java</artifactId>
      <version>2.13.0</version>
    </dependency>
  </dependencies>
</plugin>

然后执行 mvn rewrite:run 命令,OpenRewrite 就会自动将 Java 21 支持的新特性替换掉传统写法。主要修改点如下:

  1. 文本块,可以直接在代码中复制粘贴 JSON 等格式的数据,不用担心增加换行符等多余的符号了。详情见: JEP 378: Text Blocks。这是D瓜哥最喜欢的特性了。也包括上面的模式匹配增强特性。

  2. 模式匹配增强:支持在执行 instanceof 操作时,同时定义指定类型的新变量,省却了类型转换的过程。具体见: JEP 394: Pattern Matching for instanceof

  3. 排序性集合:可以直接使用 getFirst()getLast() 来获取第一个和最后一个集合元素了。当然也有对应的修改操作。详情见: JEP 431: Sequenced Collections

  4. String 类增加的新格式化方法: formatted(Object…​ args)

  5. 增加 Maven 中 Java 版本。

还有其他的一些细微修改,感兴趣请自行探索。

Base64

Base64 也是大家常用的操作,常用的实现有三种:① Apache Common Codec;② sun.misc 包下提供的实现类;③ 从 Java 1.8 开始提供的标准库 java.util.Base64。目前,第①种方案稳定;第②种方案,从 Java 9 开始,已经不再对外提供,也可以理解成无法使用了,在升级到 Java 21 时,就会报错;第③种方案是最稳妥的。所以,最佳实践就是迁移到 java.util.Base64 上。可以使用 OpenRewrite 一步到位。具体代码如下:

<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>5.30.0</version>
  <configuration>
    <activeRecipes>
      <!-- 使用 java.util.Base64 替换 sun.misc 包下的 Base64 实现类 -->
      <!-- https://docs.openrewrite.org/recipes/java/migrate/usejavautilbase64 -->
      <recipe>org.openrewrite.java.migrate.UseJavaUtilBase64</recipe>
      <!-- 使用 java.util.Base64 替换 Apache Commons Codec 库 -->
      <!-- https://docs.openrewrite.org/recipes/apache/commons/codec/apachebase64tojavabase64 -->
      <recipe>org.openrewrite.apache.commons.codec.ApacheBase64ToJavaBase64</recipe>
    </activeRecipes>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>org.openrewrite.recipe</groupId>
      <artifactId>rewrite-migrate-java</artifactId>
      <version>2.13.0</version>
    </dependency>
    <dependency>
      <groupId>org.openrewrite.recipe</groupId>
      <artifactId>rewrite-apache</artifactId>
      <version>1.2.0</version>
    </dependency>
  </dependencies>
</plugin>

类似的问题还有: Replace com.sun.net.ssl packageUse javax.net.ssl instead of com.sun.net.ssl 等。感兴趣,留给大家请自行探索。

迁移到 Spring 6

既然升级到了 Java 21,那么 Spring 也可以跟同一起升级到 Spring 6+。OpenRewrite 也通过了相关处方,相关配置如下:

<!-- @author: D瓜哥 · https://www.diguage.com -->
<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>5.30.0</version>
  <configuration>
    <activeRecipes>
      <!-- 迁移到 Spring 6 -->
      <!-- https://docs.openrewrite.org/recipes/java/spring/framework/upgradespringframework_6_0 -->
      <recipe>org.openrewrite.java.spring.framework.UpgradeSpringFramework_6_0</recipe> (1)
    </activeRecipes>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>org.openrewrite.recipe</groupId> (2)
      <artifactId>rewrite-spring</artifactId>
      <version>5.9.0</version>
    </dependency>
  </dependencies>
</plugin>
1指定激活处方。后续的升级方案,主要是在这里添加不同的处方。
2处方所在的 Jar。通过引入不同的库,就可以增加响应的处方。

配置完成后,执行 mvn rewrite:run 命令,就可以看到迁移变化。

D瓜哥尝试了一下,可能让大家见笑了,基本上没有什么大的变化,只有一些个表的小变化:

  1. 将 Maven 中 Spring 版本升级了 6.0.19(但是的最新版)。

  2. 最大改动就是 Assert 类的办法,必须加说明文字了。

不过,D瓜哥私以为这反倒是优点:这是 Spring API 稳定性的最好表现,稳定的 API 可以保证大多数 应用的无痛升级。

迁移到 Spring Boot 3.2

既然升级了 Spring,岂有不升级 Spring Boot 的道理?OpenRewrite 也提供了相关方案:

<!-- @author: D瓜哥 · https://www.diguage.com -->
<!-- 迁移到 Spring Boot 3.2 -->
<!-- https://docs.openrewrite.org/recipes/java/spring/boot3/upgradespringboot_3_2 -->
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_2</recipe> (1)
1指定处方。其余代码与上述升级 Spring 相同,省略。

升级同样不大,主要改动点如下:

  1. 上述 升级到 Java 21 中的改动点。

  2. 上述 迁移到 Spring 6 中的改动点。

  3. javax.annotation.Resource 替换为 jakarta.annotation.Resource

    这个改动点,可有可无,具体原因已经在 OpenJDK 21 升级指南:@javax.annotation.Resource 中介绍了。

  4. 升级 MySQL 的依赖,从 mysql:mysql-connector-java 升级为 com.mysql:mysql-connector-j

  5. 迁移 JUnit 到 JUnit 5。

  6. 升级 Spring Boot 等相关依赖。

改动点也不是很大,符合了 Spring 家族一向稳定可靠的风格。

迁移到 JUnit 5 及最佳实践

Spring Boot 3.x 已经将 JUnit 5 作为首要的测试框架,可以顺手把 JUnit 迁移一下:

<!-- @author: D瓜哥 · https://www.diguage.com -->
<plugin>
  <groupId>org.openrewrite.maven</groupId>
  <artifactId>rewrite-maven-plugin</artifactId>
  <version>5.30.0</version>
  <configuration>
    <activeRecipes>
      <!-- 迁移到 JUnit 5 -->
      <!-- https://docs.openrewrite.org/running-recipes/popular-recipe-guides/migrate-from-junit-4-to-junit-5 -->
      <recipe>org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration</recipe> (1)
      <!-- JUnit 5 最佳实践 -->
      <!-- https://docs.openrewrite.org/recipes/java/testing/junit5/junit5bestpractices -->
      <recipe>org.openrewrite.java.testing.junit5.JUnit5BestPractices</recipe> (1)
    </activeRecipes>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>org.openrewrite.recipe</groupId>
      <artifactId>rewrite-spring</artifactId>
      <version>5.9.0</version>
    </dependency>
    <dependency>
      <groupId>org.openrewrite.recipe</groupId>
      <artifactId>rewrite-testing-frameworks</artifactId>
      <version>2.8.0</version>
    </dependency>
  </dependencies>
</plugin>
1可以一次执行多个处方。

参考资料

未完待续!

未完待续!

未完待续!