1. Overview
1.概述
OpenRewrite is a refactoring ecosystem for Java and other source code. Sometimes, we need to upgrade dependencies to the latest versions, apply security patches, eliminate the use of a deprecated API, migrate from one technology to another (e.g., JUnit asserts to AssertJ), etc. We can use the OpenRewrite library to address these challenges. In this tutorial, we’ll discuss the basics of the OpenRewrite project and show some examples of how it can be used in practice. In every case, we’ll use the Spring PetClinic application.
OpenRewrite 是 Java 和其他源代码的重构生态系统。有时,我们需要将依赖关系升级到最新版本、应用安全补丁、取消使用已废弃的 API、从一种技术迁移到另一种技术(例如,JUnit asserts 到 AssertJ),等等。我们可以使用 OpenRewrite 库来应对这些挑战。在本教程中,我们将讨论 OpenRewrite 项目的基础知识,并举例说明如何在实践中使用它。在每个案例中,我们都将使用 Spring PetClinic 应用程序。
2. OpenRewrite Basics
2.OpenRewrite 基础知识
Here are some of the common types of upgrades we can perform with OpenRewrite:
以下是我们可以使用 OpenRewrite 执行的一些常见升级类型:
- Language Upgrades: migrating from an older version of a language to a newer version, such as Java 8 to Java 11 or beyond.
- Framework Upgrades: adapting to newer versions of frameworks like Spring Boot, Hibernate, etc.
- Dependency Migration: upgrading from one library version to another when there are breaking changes.
- Security Patching: replacing vulnerable methods or libraries with secure alternatives.
- Custom Transformations: any transformation specific to our business logic or infrastructure.
2.1. OpenRewrite Recipes
2.1.OpenRewrite Recipes
The main feature of OpenRewrite is that it will automatically refactor source code by applying recipes to the project. OpenRewrite comes with a variety of built-in recipes for common transformations, and the community often contributes recipes for widespread tasks. Each recipe can perform specific refactoring tasks. These recipes are written in Java code and included in the build process using the OpenRewrite Maven or Gradle plugin. In this tutorial, we’ll focus on the Maven plugin.
OpenRewrite的主要功能是将配方应用到项目中,从而自动重构源代码。OpenRewrite 为常见的转换提供了各种内置配方,社区也经常为广泛的任务提供配方。每个配方都可以执行特定的重构任务。这些配方用 Java 代码编写,并通过 OpenRewrite Maven 或 Gradle 插件包含在构建过程中。在本教程中,我们将重点介绍 Maven 插件。
2.2. Maven Dependency
2.2.Maven 依赖
Let’s start by importing the rewrite-maven-plugin plugin in the <plugins> section of the pom.xml:
首先,让我们在 pom.xml 的 <plugins> 部分导入 rewrite-maven-plugin 插件:
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>5.8.1</version>
<configuration>
<activeRecipes>
// Define the recipes
</activeRecipes>
</configuration>
<dependencies>
// Dependencies for recipes
</dependencies>
</plugin>
The <activeRecipes> tags specify which OpenRewrite recipes should be active when the plugin runs. Also, we can define any additional dependencies that the plugin might need. This can be particularly useful if certain recipes require external libraries or specific versions of libraries.
<activeRecipes>标记指定了插件运行时应激活哪些 OpenRewrite 配方。此外,我们还可以定义插件可能需要的其他依赖项。如果某些配方需要外部库或特定版本的库,这一点尤其有用。
2.3. Checkout Spring PetClinic
2.3.结账 Spring PetClinic
Let’s check out the relevant branch of Spring PetClinic locally:
让我们来看看 Spring PetClinic 在当地的相关分店:
git clone https://github.com/spring-projects/spring-petclinic.git;
cd spring-petclinic;
git switch -c 2.5.x 1.5.x;
Here, the Java and Spring Boot versions are 8 and 1.5, respectively. In the next sections, we’ll go through the most widely used recipes.
这里,Java 和 Spring Boot 的版本分别为 8 和 1.5。在接下来的章节中,我们将介绍使用最广泛的配方。
3. Upgrading Spring Boot
3.升级 Spring Boot
Upgrading a Spring Boot project can be a complex task, depending on the changes between the version we’re currently using and the version we want to upgrade to. However, using OpenRewrite can help us navigate the process smoothly. Let’s upgrade the PetClinic project from Spring Boot 1.5 to 2.7.
升级 Spring Boot 项目可能是一项复杂的任务,这取决于我们当前使用的版本和要升级到的版本之间的变化。不过,使用 OpenRewrite 可以帮助我们顺利完成这一过程。让我们将 PetClinic 项目从 Spring Boot 1.5 升级到 2.7。
3.1. Maven Dependency
3.1.Maven 依赖
Let’s add the UpgradeSpringBoot_2_7 recipe to the <activeRecipes> section in the rewrite-maven-plugin plugin:
让我们在 rewrite-maven-plugin 插件的 <activeRecipes> 部分中添加 UpgradeSpringBoot_2_7 配方:
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7</recipe>
</activeRecipes>
Also, we need to declare the rewrite-spring dependency to the <dependencies> section of our plugin:
此外,我们还需要在插件的 <dependencies> 部分声明 rewrite-spring 依赖项:
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>5.0.11</version>
</dependency>
Note that to upgrade to Spring Boot 3, we need to use the UpgradeSpringBoot_3_0 recipe.
请注意,要升级到 Spring Boot 3,我们需要使用 UpgradeSpringBoot_3_0 秘诀。
3.2. Run Spring Boot Upgrade Recipe
3.2.运行 Spring Boot 升级配方
Now, we’re ready to execute the migration by running this command:
现在,我们可以运行这条命令来执行迁移了:
mvn rewrite:run
Below, we can see the results of Maven:
下面,我们可以看到 Maven 的结果:
[INFO] --- rewrite-maven-plugin:5.8.1:run (default-cli) @ spring-petclinic ---
[INFO] Using active recipe(s) [org.openrewrite.java.spring.boot2.UpgradeSpringBoot_2_7]
[INFO] Using active styles(s) []
[INFO] Validating active recipes...
[INFO] Project [petclinic] Resolving Poms...
[INFO] Project [petclinic] Parsing source files
Here, we see that Maven lists active recipes and applies the pom and source file changes. After running the recipe, OpenRewrite provides a list of changes. We can inspect the results with git diff:
在这里,我们看到 Maven 列出了活动配方,并应用了 pom 和源文件更改。运行配方后,OpenRewrite 会提供更改列表。我们可以使用 git diff 检查结果:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
- <version>1.5.4.RELEASE</version>
+ <version>2.7.14</version>
</parent>
As we can see here, the spring-boot-starter-parent version has been upgraded to 2.7.14. In addition, the @Autowired has been removed from some of our Java classes during the upgrade process:
正如我们在这里看到的,spring-boot-starter-parent版本已升级到 2.7.14。此外,在升级过程中,@Autowired 已从我们的一些 Java 类中删除:
-import org.springframework.beans.factory.annotation.Autowired;
class VetController {
private final VetRepository vets;
- @Autowired
public VetController(VetRepository clinicService) {
this.vets = clinicService;
}
Note that in Spring Boot 2.x and above, if a class has a single constructor, it will automatically be used for autowiring without needing the @Autowired annotation. When we upgrade to a newer version of Spring Boot, many of the managed dependencies, including JUnit, may get upgraded as well. Spring Boot 2.x defaults to using JUnit 5, whereas Spring Boot 1.5 was aligned with JUnit 4. This means there’s an expected change in how tests are structured and run. Depending on our needs, we can just upgrade JUnit without upgrading Spring Boot. In the next section, we’ll see how to migrate from JUnit 4 to JUnit 5.
请注意,在 Spring Boot 2.x 及以上版本中,如果一个类只有一个构造函数,它将自动用于自动布线,而无需使用 @Autowired 注解。当我们升级到较新版本的 Spring Boot 时,许多托管依赖项(包括 JUnit )可能也会升级。Spring Boot 2.x 默认使用 JUnit 5,而 Spring Boot 1.5 则与 JUnit 4 保持一致。这意味着测试的结构和运行方式将发生预期的变化。根据我们的需要,我们可以只升级 JUnit 而不升级 Spring Boot。在下一节中,我们将了解如何从 JUnit 4 迁移到 JUnit 5。
4. Upgrading to JUnit5
4.升级到 JUnit5
JUnit is the de facto standard for unit testing in Java applications. OpenRewrite supports migrating from JUnit 4 to its successor JUnit 5.
JUnit 是 Java 应用程序单元测试的事实标准。OpenRewrite 支持从 JUnit 4 迁移到其后续版本 JUnit 5。
4.1. Maven Dependency
4.1.Maven 依赖
Let’s activate the JUnit5BestPractices recipe in our pom.xml:
让我们在 pom.xml 中激活 JUnit5BestPractices 配方:
<activeRecipes>
<recipe>org.openrewrite.java.testing.junit5.JUnit5BestPractices</recipe>
</activeRecipes>
Also, we need to declare the rewrite-testing-frameworks dependency to the <dependencies> section of our plugin:
此外,我们还需要在插件的 <dependencies> 部分声明 rewrite-testing-frameworks 依赖关系:
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-testing-frameworks</artifactId>
<version>2.0.12</version>
</dependency>
4.2. Run JUnit Upgrade Recipe
4.2.运行 JUnit 升级配方
Now, we execute the migration by running the mvn rewrite:run command. This command is completed in about a minute and migrates all tests to JUnit 5, as well as updating the pom.xml:
现在,我们通过运行 mvn rewrite:run 命令来执行迁移。该命令在一分钟内完成,并将所有测试迁移到 JUnit 5,同时更新 pom.xml :
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.junit.jupiter.MockitoExtension;
- @Before
- public void setup() {
+ @BeforeEach
+ void setup() {
this.petTypeFormatter = new PetTypeFormatter(pets);
}
- @Test(expected = ParseException.class)
- public void shouldThrowParseException() throws ParseException {
- Mockito.when(this.pets.findPetTypes()).thenReturn(makePetTypes());
- petTypeFormatter.parse("Fish", Locale.ENGLISH);
+ @Test
+ void shouldThrowParseException() throws ParseException {
+ assertThrows(ParseException.class, () -> {
+ Mockito.when(this.pets.findPetTypes()).thenReturn(makePetTypes());
+ petTypeFormatter.parse("Fish", Locale.ENGLISH);
+ });
}
OpenRewrite updates not just the imports and @Test annotations, but also method visibility, applies MockitoExtension, and adopts assertThrows(). In general, OpenRewrite can help JUnit migration with tasks such as:
OpenRewrite 不仅更新了导入和 @Test 注释,还更新了方法的可见性,应用了 MockitoExtension, 并采用了 assertThrows() 。一般来说,OpenRewrite 可以帮助 JUnit 迁移完成以下任务:
- Updating annotations, e.g., changing @Before to @BeforeEach or @After to @AfterEach.
- Modifying assertion method calls to align with JUnit 5’s syntax.
- Removing or replacing JUnit 4 specific features with their JUnit 5 counterparts.
- Updating import statements from JUnit 4 packages to JUnit 5 packages.
5. Upgrading to Java 11
5.升级到 Java 11
Upgrading an older source code to Java 11 can be a challenging and time-consuming issue. In this section, we’ll use OpenRewrite to perform an automated migration from Java 8 to Java 11.
将旧源代码升级到 Java 11 可能是一个具有挑战性且耗时的问题。在本节中,我们将使用 OpenRewrite 从 Java 8 自动迁移到 Java 11。
5.1. Maven Dependency
5.1.Maven 依赖关系
To upgrade Java, we need a different dependency and recipe. Let’s add the Java8toJava11 recipe to the <activeRecipes> section:
要升级 Java,我们需要不同的依赖关系和配方。让我们将 Java8toJava11 配方添加到 <activeRecipes> 部分:
<activeRecipes>
<recipe>org.openrewrite.java.migrate.Java8toJava11</recipe>
</activeRecipes>
Also, we need to declare the rewrite-migrate-java dependency to our plugin:
此外,我们还需要向插件声明 rewrite-migrate-java 依赖关系:
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-migrate-java</artifactId>
<version>2.1.1</version>
</dependency>
Note that to upgrade to Java 17, we need to use the UpgradeToJava17 recipe.
请注意,要升级到 Java 17,我们需要使用 UpgradeToJava17 方法。
5.2. Run Java Upgrade Recipe
5.2 运行 Java 升级配方
To upgrade the Java, we can apply the mvn rewrite:run command. After that, we’ll see output like:
要升级 Java,我们可以使用 mvn rewrite:run 命令。之后,我们会看到类似的输出:
[INFO] Using active recipe(s) [org.openrewrite.java.migrate.Java8toJava11]
[INFO] Running recipe(s)...
[WARNING] Changes have been made to pom.xml by:
[WARNING] org.openrewrite.java.migrate.Java8toJava11
[WARNING] org.openrewrite.java.migrate.javax.AddJaxbDependencies
[WARNING] org.openrewrite.java.dependencies.AddDependency: {groupId=jakarta.xml.bind, artifactId=jakarta.xml.bind-api, version=2.3.x, onlyIfUsing=javax.xml.bind..*, acceptTransitive=true}
[WARNING] org.openrewrite.maven.AddDependency: {groupId=jakarta.xml.bind, artifactId=jakarta.xml.bind-api, version=2.3.x, onlyIfUsing=javax.xml.bind..*, acceptTransitive=true}
[WARNING] org.openrewrite.java.migrate.javax.AddJaxbRuntime: {runtime=glassfish}
[WARNING] org.openrewrite.java.migrate.javax.AddJaxbRuntime$AddJaxbRuntimeMaven
[WARNING] org.openrewrite.java.migrate.cobertura.RemoveCoberturaMavenPlugin
[WARNING] org.openrewrite.maven.RemovePlugin: {groupId=org.codehaus.mojo, artifactId=cobertura-maven-plugin}
[WARNING] org.openrewrite.java.migrate.wro4j.UpgradeWro4jMavenPluginVersion
[WARNING] org.openrewrite.maven.UpgradePluginVersion: {groupId=ro.isdc.wro4j, artifactId=wro4j-maven-plugin, newVersion=1.10.1}
[WARNING] org.openrewrite.java.migrate.JavaVersion11
[WARNING] org.openrewrite.java.migrate.UpgradeJavaVersion: {version=11}
The Java8toJava11 recipe produces the following main relevant changes:
Java8toJava11 配方会产生以下主要相关更改:
- <java.version>1.8</java.version>
+ <java.version>11</java.version>
- <wro4j.version>1.8.0</wro4j.version>
+ <wro4j.version>1.10.1</wro4j.version>
+ <dependency>
+ <groupId>jakarta.xml.bind</groupId>
+ <artifactId>jakarta.xml.bind-api</artifactId>
+ <version>2.3.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jaxb</groupId>
+ <artifactId>jaxb-runtime</artifactId>
+ <version>2.3.8</version>
+ <scope>provided</scope>
+ </dependency>
Here, we see that the java.version Maven property is changed to 11, which sets maven.compiler.source and maven.compiler.target. This unlocks Java 11 language features such as var. Along the way, wro4j is updated to their latest version as well. Wro4j is a popular open-source project for optimizing and managing web resources in Java applications, such as JavaScript and CSS files. In addition, when migrating from Java 8 to Java 11, one of the major changes is the removal of the javax.xml.bind module from the JDK. Many applications that rely on this module for JAXB functionality need to add third-party dependencies to retain this functionality. One such replacement is the jakarta.xml.bind-api dependency.
在这里,我们看到 java.version Maven 属性已更改为 11,从而设置了 maven.compiler.source 和 maven.compiler.target 。这将解锁 Java 11 语言功能,如 var。同时,wro4j 也更新到了最新版本。Wro4j 是一个流行的开源项目,用于优化和管理 Java 应用程序中的网络资源,例如 JavaScript 和 CSS 文件。此外,从 Java 8 迁移到 Java 11 时,其中一个主要变化是从 JDK 中移除 javax.xml.bind 模块。许多依赖该模块实现 JAXB 功能的应用程序需要添加第三方依赖项来保留该功能。其中一个替代模块就是 jakarta.xml.bind-api 依赖项。
6. Conclusion
6.结论
In this tutorial, we have provided a step-by-step guide on leveraging the OpenRewrite project to seamlessly upgrade versions of Spring Boot, JUnit, and Java within the Spring PetClinic codebase. By following this tutorial, we can gain insights into the intricacies of the upgrade process, ensuring smoother transitions and better code compatibility for these core components.
在本教程中,我们将逐步介绍如何利用 OpenRewrite 项目在 Spring PetClinic 代码库中无缝升级 Spring Boot、JUnit 和 Java 的版本。通过学习本教程,我们可以深入了解升级过程的复杂性,确保这些核心组件的过渡更加顺畅,代码兼容性更好。