ソフトウェアエンジニアの日常の雑記

日々思ったことをまとめます

OpenRewriteでマルチプロジェクト構成のSpringBoot2のマイグレーションを行う

本エントリーはJava Advent Calendarの21日目です。昨日はHatanoさんの文鳥は家鴨の夢を見るか #Java - Qiitaでした。

OpenRewriteを使用して、MultiProject構成のSpringBoot2をSpringBoot3にマイグレーションを行います。所属している事業部ではよく用いられているSpringBoot x Gradle x マルチプロジェクト 構成に対して、マイグレーションを実施します。弊社でもOpenRewriteを使用してマイグレーションを行っています。

docs.openrewrite.org

ディレクト

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.4Java17となっています。

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の設定を追加します。 マルチプロジェクト構成でも、rootprojectsubprojectsのディレクティブの中に書くのではなく、トップ階層に書くのがポイントです。

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_3UpgradeToJava21を適用します。dependenciesには、それぞれの依存関係を書いておきます。

今回使用したレシピ以外にも、他の言語のものもあったりするので、探してみてください。

docs.openrewrite.org

OpenRewriteを実行する

./gradlew rewriteDryRun   // <-- コードは変更されません
./gradlew rewriteRun  // <-- コードが変更されます

実行後の差分

キャプチャで見づらいかもですが、SpringBootのバージョン変更、java tool chain のバージョン変更、javax -> jakarta パッケージの変更や、String.format -> "text".formatted など変更されています。

SpringBoot3へのマイグレーション

まとめ

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