使用 OpenRewrite 简化 Java 和 SpringBoot 迁移

[复制链接]
发表于 2025-9-23 02:37:10 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
各人好,这里是架构资源栈!点击上方关注,添加“星标”,一起学习大厂前沿架构!

移民的寻衅

随着 Spring Boot 2.x 等旧版本即将到期且不再得到支持,迁移到较新版本对于安全性、兼容性和性能改进至关重要。但是,迁移过程面临着一些寻衅:
1. 庞大变动:重要版本升级通常会引入庞大变动。比方,Spring Boot 3.x 需要Java 17并从 迁移javax.*到jakarta.* packages。
**2. 弃用的 API:**很多常用的 API 和模式已被弃用并需要替换。
**3. 手动更新:**传统迁移需要手动更新依赖项、重构代码和修复兼容性标题。
**4. 耗时:**大型代码库可能需要数周或数月才能迁移,从而增加项目成本和风险。
**5. 测试负担:**必须彻底测试每个更改以确保功能保持齐备。
那么,我们怎样才能简化和加速迁移过程呢?这就是 OpenRewrite 发挥作用的地方。
开放重写

OpenRewrite是一款开源的自动化代码重构工具,可资助开辟职员淘汰技术负担。它为框架迁移、安全修复和代码样式提供了预构建的重构方案,将工作量从几小时缩短到几分钟。
Gradle 和 Maven 插件可轻松将这些更改应用于存储库。OpenRewrite 社区最初专注于 Java,现在正在积极扩展对更多语言和框架的支持。
重要特点


  • **自动重构:**自动更新代码语法、依赖关系和模式
  • **基于配方:**使用声明性配方来界说转换规则
  • **风格保存:**保存原始代码格式和解释
  • **大规模变动:**可以划一地处置惩罚整个代码库
  • **可扩展:**支持针对特定迁移需求的自界说配方
它是怎样工作的?



  • OpenRewrite 修改代表源代码的无损语义树 (LST),并将它们转换回源代码。
  • 您可以检察更改并根据需要提交。
  • 修改是使用访问者进行的,访问者被分组到菜谱中。
  • 配方可确保变革最小,并保持原始格式。
实践

在本文中,我将演示怎样使用 OpenRewrite 将使用 Java 8、Spring Boot 2.x 和 JUnit 4 构建的简朴 CRUD Spring Boot 应用步伐迁移到 Java 21、Spring Boot 3.3 和 JUnit 5。
代码库

1)pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4.          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  5.     <modelVersion>4.0.0</modelVersion>
  6.     <parent>
  7.         <groupId>org.springframework.boot</groupId>
  8.         <artifactId>spring-boot-starter-parent</artifactId>
  9.         <version>2.7.14</version>
  10.         <relativePath/>
  11.     </parent>
  12.     <groupId>com.example</groupId>
  13.     <artifactId>demo</artifactId>
  14.     <version>0.0.1-SNAPSHOT</version>
  15.     <name>demo</name>
  16.     <description>Demo project for Spring Boot Migration</description>
  17.     <properties>
  18.         <java.version>1.8</java.version>
  19.     </properties>
  20.     <dependencies>
  21.         // dependencies: starter web, data-jpa, etc
  22.     </dependencies>
  23.     <build>
  24.         <plugins>
  25.             <plugin>
  26.                 <groupId>org.springframework.boot</groupId>
  27.                 <artifactId>spring-boot-maven-plugin</artifactId>
  28.             </plugin>
  29.         </plugins>
  30.     </build>
  31. </project>
复制代码
Enter fullscreen mode Exit fullscreen mode
2)UserController.java

  1. @RestController
  2. @RequestMapping("/api/users")
  3. public class UserController {
  4.     @Autowired
  5.     private UserRepository userRepository;
  6.     @Autowired
  7.     private UserService userService;
  8.     @RequestMapping(method = RequestMethod.GET)
  9.     public List<User> getAllUsers() {
  10.         return userRepository.findAll();
  11.     }
  12.     @RequestMapping(method = RequestMethod.POST)
  13.     public ResponseEntity<?> createUser(@Valid @RequestBody User user) {
  14.         User savedUser = userRepository.save(user);
  15.         return ResponseEntity.ok().build();
  16.     }
  17.     @RequestMapping(value = "/{id}", method = RequestMethod.GET)
  18.     public ResponseEntity<User> getUserById(@PathVariable("id") Long id) {
  19.         User user = userRepository.findById(id).orElse(null);
  20.         return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
  21.     }
  22.     @RequestMapping(value = "/username")
  23.     public ResponseEntity<User> getUserByUsername(@RequestParam String username) {
  24.         User user = userService.findByUsername(username);
  25.         return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
  26.     }
  27. }
复制代码
Enter fullscreen mode Exit fullscreen mode
3)UserService.java

  1. @Service
  2. public class UserService {
  3.     @Autowired
  4.     private UserRepository userRepository;
  5.     public User findByUsername(String username) {
  6.         return userRepository.findByUsernameNative(username);
  7.     }
  8. }
复制代码
Enter fullscreen mode Exit fullscreen mode
4)UserRepository.java

  1. @Repository
  2. public interface UserRepository extends JpaRepository<User, Long> {
  3.     @Query(value = "SELECT * FROM users WHERE username = ?1", nativeQuery = true)
  4.     User findByUsernameNative(String username);
  5. }
复制代码
Enter fullscreen mode Exit fullscreen mode
5)用户.java

  1. import javax.persistence.*;
  2. import javax.validation.constraints.NotNull;
  3. @Entity
  4. @Table(name = "users")
  5. public class User {
  6.     @Id
  7.     @GeneratedValue(strategy = GenerationType.AUTO)
  8.     private Long id;
  9.     @NotNull
  10.     @Column(nullable = false)
  11.     private String username;
  12.     @Column
  13.     private String email;
  14.     // setter, getter
  15. }
复制代码
Enter fullscreen mode Exit fullscreen mode
6)UserControllerTest.java

  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. @AutoConfigureMockMvc
  4. public class UserControllerTest {
  5.     @Autowired
  6.     private MockMvc mockMvc;
  7.     @Autowired
  8.     private UserRepository userRepository;
  9.     @Before
  10.     public void setup() {
  11.         userRepository.deleteAll();
  12.     }
  13.     @Test
  14.     public void testCreateUser() throws Exception {
  15.         String userJson = "{"username":"testuser","email":"[email protected]"}";
  16.         mockMvc.perform(MockMvcRequestBuilders.post("/api/users")
  17.                 .contentType(MediaType.APPLICATION_JSON)
  18.                 .content(userJson))
  19.                 .andExpect(MockMvcResultMatchers.status().isOk());
  20.     }
  21.     @Test
  22.     public void testGetUser() throws Exception {
  23.         User user = new User();
  24.         user.setUsername("testuser");
  25.         user.setEmail("[email protected]");
  26.         userRepository.save(user);
  27.         mockMvc.perform(MockMvcRequestBuilders.get("/api/users/" + user.getId()))
  28.                 .andExpect(MockMvcResultMatchers.status().isOk())
  29.                 .andExpect(MockMvcResultMatchers.jsonPath("$.username").value("testuser"));
  30.     }
  31. }
复制代码
Enter fullscreen mode Exit fullscreen mode
手动迁移

在使用 OpenRewrite 之前,让我们看看如果手动迁移到 Java 21 和 Spring Boot 3.3 会发生什么。
起首,更新pom.xml
  1. <parent>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-parent</artifactId>
  4.     <version>3.3.10</version>
  5.     <relativePath/>
  6. </parent>
  7. <properties>
  8.     <java.version>21</java.version>
  9. </properties>
复制代码
Enter fullscreen mode Exit fullscreen mode
接下来,使用 Maven 清算并构建项目:
mvn clean install
有几个编译错误

好的,让我们恢复更改并使用 OpenRewrite 进行迁移。
使用 OpenRewrite 进行迁移

添加 OpenRewrite 插件

在 中pom.xml,添加 OpenRewrite Maven 插件。如果您使用的是 Gradle,则可以添加 Gradle 插件。
  1. <build>
  2.     <plugins>
  3.         <plugin>
  4.             <groupId>org.openrewrite.maven</groupId>
  5.             <artifactId>rewrite-maven-plugin</artifactId>
  6.             <version>6.3.2</version>
  7.         </plugin>
  8.     </plugins>
  9. </build>
复制代码
Enter fullscreen mode Exit fullscreen mode
选择迁移方案

要发现全部可用的配方,您可以查抄配方目次,此中列出了全部可用的配方,包罗:Java、Spring Boot、Hibernate、Quarkus、Scala、.NET、Jenkins 等。
在这个展示中,我将使用三个配方:Java 8 到 21、Spring 2 到 3 和 JUnit 4 到 5。
让我们将食谱添加到 pom.xml 中;每个食谱都有自己的依赖项。
  1. <plugin>
  2.     <groupId>org.openrewrite.maven</groupId>
  3.     <artifactId>rewrite-maven-plugin</artifactId>
  4.     <version>6.3.2</version>
  5.     <configuration>
  6.         <exportDatatables>true</exportDatatables>
  7.         <activeRecipes>
  8.             <recipe>org.openrewrite.java.migrate.UpgradeToJava21</recipe>
  9.             <recipe>org.openrewrite.java.spring.boot2.SpringBoot2JUnit4to5Migration</recipe>
  10.             <recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3</recipe>
  11.         </activeRecipes>
  12.     </configuration>
  13.     <dependencies>
  14.         <dependency>
  15.             <groupId>org.openrewrite.recipe</groupId>
  16.             <artifactId>rewrite-migrate-java</artifactId>
  17.             <version>3.4.0</version>
  18.         </dependency>
  19.         <dependency>
  20.             <groupId>org.openrewrite.recipe</groupId>
  21.             <artifactId>rewrite-spring</artifactId>
  22.             <version>6.3.0</version>
  23.         </dependency>
  24.     </dependencies>
  25. </plugin>
复制代码
Enter fullscreen mode Exit fullscreen mode
现在,运行 Maven 来安装 OpenRewrite 插件及其配方:
mvn clean install
预览迁移

OpenRewrite 提供了一种dryRun模式,允许开辟职员在现实应用更改之前通过以下方式预览更改:
mvn rewrite:dryRun
您可以看到将用于现实迁移的更改。

应用迁移

现在,让我们进行迁移
mvn rewrite:run
使用您的 IDE 或差异查抄工具来查抄更改。
1.更新pom.xml:自动更新SpringBoot版本到3.3.10,Java版本到21,删除JUnit4。

2. 更新控制器:
从 迁移javax.*到jakarta.* packages,改为使用@GetMapping,@PostMapping用于专用解释。

3. 更新单元测试:
替换@Before为@BeforeEach,更新包名称

限定

在这个展示中,OpenRewrite 乐成迁移到 Java 21 和 Spring Boot 3.3,但它仍旧存在一些限定。
由于 OpenRewrite 依赖于预界说的配方,因此它支持很多常见框架,但并非全部。
比方,如果您需要将第三方库(如 Ehcache2)迁移到 Ehcache3(Spring 3.0 中不再支持),OpenRewrite 不提供内置配方。在这种情况下,您必须编写自界说配方或手动执行迁移。
如果您创建了自界说配方,请思量将其贡献给 OpenRewrite 社区,以资助其他进行雷同迁移的人。
概括

OpenRewrite 通过以下方式显著简化了 Java 和 Spring Boot 迁移过程:


  • 自动重复代码更改
  • 淘汰迁移时间和精力
  • 最大限度地淘汰人为错误
  • 尺度化迁移方法
固然 OpenRewrite并不能消除测试和验证的需要,但它淘汰了迁移所需的手动工作量。这使开辟职员可以专注于更复杂的迁移方面和业务逻辑更新。
参考


  • https://docs.openrewrite.org/
  • https://github.com/openrewrite
   原文地点:https://mp.weixin.qq.com/s/c7HMXbTJdxYWj53tKdY3hA
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表