mcpField
J
java-migration

java-migration

github.com/decebals開発761333 回インストール7 週間前に更新
リポジトリ

java-migration について

Java プロジェクトをアップグレードするための段階的なガイドです。現状確認からビルド設定の変更、テストまでを扱い、Java と Spring Boot の互換性変更や確認項目を示します。

主な機能

  1. 01Java バージョンの移行経路
  2. 02段階的な移行ワークフロー
  3. 03バージョン変更と削除 API の説明
  4. 04Spring Boot 2.x から 3.x への指針
  5. 05移行チェックリストとコマンド

活用シーン

  1. 01既存プロジェクトを新しい Java LTS に更新する
  2. 02更新後のコンパイルや実行時の問題を調べる
  3. 03Spring Boot と Jakarta パッケージを移行する
  4. 04移行前後の確認項目を整理する

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/decebals/claude-code-java --skill java-migration

インストール後、Claude Code で/java-migrationを入力して読み込まれたか確認します。

同梱ファイルREADME.md

詳細

Java 移行スキル

Java プロジェクトをメジャーバージョン間でアップグレードするためのステップごとのガイドです。

使用するタイミング

  • ユーザーが「Java 25 にアップグレードして」「Java 8 から移行して」「Java のバージョンを更新して」と言った場合
  • レガシープロジェクトのモダナイズ
  • Spring Boot 2.x → 3.x → 4.x の移行
  • LTS バージョンの採用準備

移行パス

Java 8 (LTS) → Java 11 (LTS) → Java 17 (LTS) → Java 21 (LTS) → Java 25 (LTS)
     │              │               │              │               │
     └──────────────┴───────────────┴──────────────┴───────────────┘
                         Always migrate LTS → LTS

クイックリファレンス:変更が必要な点

From → To主な破壊的変更
8 → 11javax.xml.bind の削除、モジュールシステム、内部 API
11 → 17シールドクラス(プレビューから正式版へ)、強力なカプセル化
17 → 21パターンマッチングの変更、finalize() の削除に向けた非推奨化
21 → 25Security Manager の削除、Unsafe メソッドの削除、32 ビットのサポート終了

移行ワークフロー

ステップ 1:現在の状態を評価する
# Check current Java version
java -version

# Check compiler target in Maven
grep -r "maven.compiler" pom.xml

# Find usage of removed APIs
grep -r "sun\." --include="*.java" src/
grep -r "javax\.xml\.bind" --include="*.java" src/
ステップ 2:ビルド設定を更新する

Maven:

<properties>
    <java.version>21</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
</properties>


<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.12.1</version>
    <configuration>
        <release>21</release>
    </configuration>
</plugin>

Gradle:

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}
ステップ 3:コンパイルエラーを修正する

コンパイルを実行し、エラーを繰り返し修正します:

mvn clean compile 2>&1 | head -50
ステップ 4:テストを実行する
mvn test
ステップ 5:実行時の警告を確認する
# Run with illegal-access warnings
java --illegal-access=warn -jar app.jar

Java 8 → 11 の移行

削除された API
削除されたもの代替手段
javax.xml.bind (JAXB)依存関係を追加:jakarta.xml.bind-api + jaxb-runtime
javax.activation依存関係を追加:jakarta.activation-api
javax.annotation依存関係を追加:jakarta.annotation-api
java.corba代替手段なし(ほとんど使用されません)
java.transaction依存関係を追加:jakarta.transaction-api
sun.misc.Base64*java.util.Base64 を使用
sun.misc.Unsafe(一部)可能な場合は VarHandle を使用
不足している依存関係を追加する(Maven)

<dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>4.0.4</version>
    <scope>runtime</scope>
</dependency>


<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>
モジュールシステムの問題

JDK 内部へのリフレクションを使用している場合は、JVM フラグを追加します:

--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED

Maven Surefire:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>
            --add-opens java.base/java.lang=ALL-UNNAMED
        </argLine>
    </configuration>
</plugin>
導入できる新機能
// var (local variable type inference)
var list = new ArrayList<String>();  // instead of ArrayList<String> list = ...

// String methods
"  hello  ".isBlank();      // true for whitespace-only
"  hello  ".strip();        // better trim() (Unicode-aware)
"line1\nline2".lines();     // Stream<String>
"ha".repeat(3);             // "hahaha"

// Collection factory methods (Java 9+)
List.of("a", "b", "c");     // immutable list
Set.of(1, 2, 3);            // immutable set
Map.of("k1", "v1");         // immutable map

// Optional improvements
optional.ifPresentOrElse(
    value -> process(value),
    () -> handleEmpty()
);

// HTTP Client (replaces HttpURLConnection)
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com"))
    .build();
HttpResponse<String> response = client.send(request,
    HttpResponse.BodyHandlers.ofString());

Java 11 → 17 の移行

破壊的変更
変更影響
強力なカプセル化--illegal-access は機能しなくなります。明示的な --add-opens を使用する必要があります
シールドクラス(正式版)プレビュー機能を使用していた場合
instanceof のパターンマッチングプレビュー版から正式版への構文変更
導入できる新機能
// Records (immutable data classes)
public record User(String name, String email) {}
// Auto-generates: constructor, getters, equals, hashCode, toString

// Sealed classes
public sealed class Shape permits Circle, Rectangle {}
public final class Circle extends Shape {}
public final class Rectangle extends Shape {}

// Pattern matching for instanceof
if (obj instanceof String s) {
    System.out.println(s.length());  // s already cast
}

// Switch expressions
String result = switch (day) {
    case MONDAY, FRIDAY -> "Work";
    case SATURDAY, SUNDAY -> "Rest";
    default -> "Midweek";
};

// Text blocks
String json = """
    {
        "name": "John",
        "age": 30
    }
    """;

// Helpful NullPointerException messages
// a.b.c.d() → tells exactly which part was null

Java 17 → 21 の移行

破壊的変更
変更影響
パターンマッチング switch(正式版)プレビュー版との小さな構文上の違い
finalize() の削除に向けた非推奨化Cleaner または try-with-resources に置き換えます
UTF-8 がデフォルトプラットフォームのエンコーディングを前提としている場合、ファイル読み込みに影響する可能性があります
導入できる新機能
// Virtual Threads (Project Loom) - MAJOR
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> handleRequest());
}
// Or simply:
Thread.startVirtualThread(() -> doWork());

// Pattern matching in switch
String formatted = switch (obj) {
    case Integer i -> "int: " + i;
    case String s -> "string: " + s;
    case null -> "null value";
    default -> "unknown";
};

// Record patterns
record Point(int x, int y) {}
if (obj instanceof Point(int x, int y)) {
    System.out.println(x + ", " + y);
}

// Sequenced Collections
List<String> list = new ArrayList<>();
list.addFirst("first");    // new method
list.addLast("last");      // new method
list.reversed();           // reversed view

// String templates (preview in 21)
// May need --enable-preview

// Scoped Values (preview) - replace ThreadLocal
ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();
ScopedValue.where(CURRENT_USER, user).run(() -> {
    // CURRENT_USER.get() available here
});

Java 21 → 25 の移行

破壊的変更
変更影響
Security Manager の削除Security Manager に依存するアプリケーションには、別のセキュリティ対策が必要です
sun.misc.Unsafe メソッドの削除代わりに VarHandle または FFM API を使用します
32 ビットプラットフォームのサポート終了x86-32 はサポートされなくなります
レコードパターン変数が finalswitch 内でパターン変数を再代入できません
ScopedValue.orElse(null) が禁止null 以外のデフォルト値を指定する必要があります
動的エージェントの制限-XX:+EnableDynamicAgentLoading フラグが必要です
Unsafe の使用状況を確認する
# Find sun.misc.Unsafe usage
grep -rn "sun\.misc\.Unsafe" --include="*.java" src/

# Find Security Manager usage
grep -rn "SecurityManager\|System\.getSecurityManager" --include="*.java" src/
導入できる新機能
// Scoped Values (FINAL in Java 25) - replaces ThreadLocal
private static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();

public void handleRequest(User user) {
    ScopedValue.where(CURRENT_USER, user).run(() -> {
        processRequest();  // CURRENT_USER.get() available here and in child threads
    });
}

// Structured Concurrency (Preview, redesigned API in 25)
try (StructuredTaskScope.ShutdownOnFailure scope = StructuredTaskScope.open()) {
    Subtask<User> userTask = scope.fork(() -> fetchUser(id));
    Subtask<Orders> ordersTask = scope.fork(() -> fetchOrders(id));

    scope.join();
    scope.throwIfFailed();

    return new Profile(userTask.get(), ordersTask.get());
}

// Stable Values (Preview) - lazy initialization made easy
private static final StableValue<ExpensiveService> SERVICE =
    StableValue.of(() -> new ExpensiveService());

public void useService() {
    SERVICE.get().doWork();  // Initialized on first access, cached thereafter
}

// Compact Object Headers - automatic, no code changes
// Objects now use 64-bit headers instead of 128-bit (less memory)

// Primitive Patterns in instanceof (Preview)
if (obj instanceof int i) {
    System.out.println("int value: " + i);
}

// Module Import Declarations (Preview)
import module java.sql;  // Import all public types from module
パフォーマンスの改善(自動)

Java 25 には、いくつかの自動的なパフォーマンス改善が含まれています:

  • コンパクトオブジェクトヘッダー:オブジェクトあたり 16 バイトから 8 バイトに削減
  • String.hashCode() の定数畳み込み:String キーを使用する Map の検索が高速化
  • AOT クラス読み込み:事前キャッシュにより起動が高速化
  • 世代別 Shenandoah GC:スループットが向上し、一時停止時間が短縮
OpenRewrite による移行
# Automated Java 25 migration
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:LATEST \
  -Drewrite.activeRecipes=org.openrewrite.java.migrate.UpgradeToJava25

Spring Boot の移行

Spring Boot 2.x → 3.x

要件:

  • Java 17+(必須)
  • Jakarta EE 9+(javax.* → jakarta.*)

パッケージ名の変更:

// Before (Spring Boot 2.x)
import javax.persistence.*;
import javax.validation.*;
import javax.servlet.*;

// After (Spring Boot 3.x)
import jakarta.persistence.*;
import jakarta.validation.*;
import jakarta.servlet.*;

検索と置換:

# Find all javax imports that need migration
grep -r "import javax\." --include="*.java" src/ | grep -v "javax.crypto" | grep -v "javax.net"

自動移行:

# Use OpenRewrite
mvn -U org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:LATEST \
  -Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0
依存関係の更新(Spring Boot 3.x)
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.2</version>
</parent>



Hibernate 5 → 6 の変更点
// ID generation strategy changed
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)  // preferred
private Long id;

// Query changes
// Before: createQuery returns raw type
// After: createQuery requires type parameter

// Before
Query query = session.createQuery("from User");

// After
TypedQuery<User> query = session.createQuery("from User", User.class);

よくある移行時の問題

問題:リフレクションへのアクセスが拒否される

症状:

java.lang.reflect.InaccessibleObjectException: Unable to make field accessible

修正方法:

--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
問題:JAXB の ClassNotFoundException

症状:

java.lang.ClassNotFoundException: javax.xml.bind.JAXBContext

修正方法: JAXB の依存関係を追加します(Java 8→11 のセクションを参照)

問題:Lombok が動作しない

修正方法: Lombok を最新バージョンに更新します:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
</dependency>
問題:Mockito でテストが失敗する

修正方法: Mockito を更新します:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>5.8.0</version>
    <scope>test</scope>
</dependency>

移行チェックリスト

移行前
  • 現在の Java バージョンを記録する
  • すべての依存関係とそのバージョンを一覧にする
  • 内部 API(sun.*、com.sun.*)の使用箇所を特定する
  • フレームワークの互換性を確認する(Spring、Hibernate など)
  • バックアップを作成する/ブランチを作成する
移行中
  • ビルドツールの設定を更新する
  • 不足している Jakarta の依存関係を追加する
  • javax.* → jakarta.* のインポートを修正する(Spring Boot 3 の場合)
  • 必要に応じて --add-opens フラグを追加する
  • Lombok、Mockito、その他のツールを更新する
  • コンパイルエラーを修正する
  • テストを実行する
移行後
  • 不要な --add-opens フラグを削除する
  • 新しい言語機能(レコード、var など)を採用する
  • CI/CD パイプラインを更新する
  • 実施した変更を記録する

クイックコマンド

# Check Java version
java -version

# Find internal API usage
grep -rn "sun\.\|com\.sun\." --include="*.java" src/

# Find javax imports (for Jakarta migration)
grep -rn "import javax\." --include="*.java" src/

# Compile and show first errors
mvn clean compile 2>&1 | head -100

# Run with verbose module warnings
java --illegal-access=debug -jar app.jar

# OpenRewrite Spring Boot 3 migration
mvn org.openrewrite.maven:rewrite-maven-plugin:run \
  -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:LATEST \
  -Drewrite.activeRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0

バージョン互換性マトリックス

フレームワークJava 8Java 11Java 17Java 21Java 25
Spring Boot 2.7.x✅✅✅⚠️❌
Spring Boot 3.2.x❌❌✅✅✅
Spring Boot 3.4+❌❌✅✅✅
Hibernate 5.6✅✅✅⚠️❌
Hibernate 6.4+❌❌✅✅✅
JUnit 5.10+✅✅✅✅✅
Mockito 5+❌✅✅✅✅
Lombok 1.18.34+✅✅✅✅✅

LTS サポートのタイムライン:

  • Java 21:Oracle の無料サポートは 2028 年 9 月まで
  • Java 25:Oracle の無料サポートは 2033 年 9 月まで

コメント