本エントリーはJava Advent Calendarの21日目です。昨日はHatanoさんの文鳥は家鴨の夢を見るか #Java - Qiitaでした。
OpenRewriteを使用して、MultiProject構成のSpringBoot2をSpringBoot3にマイグレーションを行います。所属している事業部ではよく用いられているSpringBoot x Gradle x マルチプロジェクト 構成に対して、マイグレーションを実施します。弊社でもOpenRewriteを使用してマイグレーションを行っています。
ディレクトリ
appディレクトリとserviceディレクトリの2つのマルチプロジェクト構成になっています。
├── app │ ├── build │ ├── build.gradle │ └── src ├── build.gradle ├── gradlew ├── gradlew.bat ├── service │ ├── build │ ├── build.gradle │ └── src └── settings.gradle
build.gradle
build.gradleは、下記にようになっており、SpringBoot2.7.4、Java17となっています。
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.4' apply false
id 'io.spring.dependency-management' version '1.0.11.RELEASE' apply false
}
group = 'jp.co.excite'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.springframework.boot'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
}
project(":app") {
dependencies {
implementation project(":service")
implementation 'org.springframework.boot:spring-boot-starter-web'
}
}
project(":service") {
repositories {
mavenCentral()
}
}
コード
入力文字を大文字にするAPIを用意します。
コントローラは下記のようになります。
package jp.co.excite.adventcalendar.controller;
import jp.co.excite.adventcalendar.SampleService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.constraints.NotBlank;
@RestController
@RequestMapping("/")
public class RootController {
private final SampleService sampleService;
public RootController(SampleService sampleService) {
this.sampleService = sampleService;
}
@RequestMapping("upper")
public String upper(@NotBlank String text) {
return sampleService.upper(text);
}
}
サービスは下記のようになります。
package jp.co.excite.adventcalendar;
import org.springframework.stereotype.Service;
public interface SampleService {
String upper(String text);
}
@Service
class SampleServiceImpl implements SampleService {
@Override
public String upper(String text) {
return String.format("input: %s", text.toUpperCase());
}
}
上記のAPIにOpenRewriteでマイグレーションをかけていきます。
build.gradleにOpenRewriteの設定を追加
OpenRewriteの設定を追加します。
マルチプロジェクト構成でも、rootprojectやsubprojectsのディレクティブの中に書くのではなく、トップ階層に書くのがポイントです。
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.4' apply false
id 'io.spring.dependency-management' version '1.0.11.RELEASE' apply false
id 'org.openrewrite.rewrite' version '6.28.3' apply false // <-- 追加
}
/* 下記を追加 */
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
apply plugin: 'org.openrewrite.rewrite'
rewrite {
activeRecipe("org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3")
activeRecipe("org.openrewrite.java.migrate.UpgradeToJava21")
}
repositories {
mavenCentral()
}
dependencies {
rewrite("org.openrewrite.recipe:rewrite-spring:5.25.0")
rewrite("org.openrewrite.recipe:rewrite-migrate-java:2.31.0")
}
/* ここまで追加 */
今回は、SpringBoot3.3にするのに追加して、Java21にもアップデートします。
設定は、 rewriteの中に適用するレシピを記述します。今回は、UpgradeSpringBoot_3_3とUpgradeToJava21を適用します。dependenciesには、それぞれの依存関係を書いておきます。
今回使用したレシピ以外にも、他の言語のものもあったりするので、探してみてください。
OpenRewriteを実行する
./gradlew rewriteDryRun // <-- コードは変更されません ./gradlew rewriteRun // <-- コードが変更されます
実行後の差分
キャプチャで見づらいかもですが、SpringBootのバージョン変更、java tool chain のバージョン変更、javax -> jakarta パッケージの変更や、String.format -> "text".formatted など変更されています。

まとめ
小さいサンプルコードなので変更は少ないですけど、実際は、2000ファイルや4000ファイルくらい変更がある場合もあります。そして、ほぼすぐ動作しますので単体テストを通して、ある程度負荷とテストとかして1週間もあればマイグレーションはできると思います。こういうツールが充実しているのはサービスを運営していく身としては大変助かります。