Spring Boot 3.4:新機能を徹底解説

Spring Boot 3.4はネイティブ構造化ロギング、拡張されたバーチャルスレッド、デフォルトのグレースフルシャットダウン、MockMvcTesterを導入。新機能の完全ガイド。

Spring Boot 3.4の新機能と改善点

Spring Boot 3.4は2024年11月にリリースされ、開発者の生産性とアプリケーションパフォーマンスにおいて大幅な改善を提供します。このバージョンではネイティブ構造化ロギングが導入され、グレースフルシャットダウンがデフォルトで有効になり、バーチャルスレッドのサポートがフレームワーク全体に拡張されています。

前提条件

Spring Boot 3.4はJava 17以上が必要で、バーチャルスレッドにはJava 21をサポートしています。このバージョンはSpring Framework 6.2を使用しています。

ネイティブ構造化ロギング

構造化ロギングは、アプリケーションの可観測性における重要な進歩です。解析が困難なテキストベースのログの代わりに、Spring Boot 3.4はElasticsearch、Grafana Loki、Datadogなどのツールで処理可能なJSON形式のログを生成します。

3つのフォーマットがネイティブでサポートされています:Elastic Common Schema(ECS)、Logstash、Graylog Extended Log Format(GELF)。

properties
# application.properties
# Enable structured logging in console
logging.structured.format.console=ecs

# Or for log files
logging.structured.format.file=logstash

このシンプルな設定で、自動的にフォーマットされた構造化JSONログが生成されます。

LoggingController.javajava
@RestController
@RequestMapping("/api/demo")
public class LoggingController {

    // Logger injection via SLF4J
    private static final Logger logger = LoggerFactory.getLogger(LoggingController.class);

    @GetMapping("/action")
    public ResponseEntity<String> performAction(@RequestParam String userId) {
        // Log will be automatically formatted as structured JSON
        logger.info("Action performed by user: {}", userId);
        return ResponseEntity.ok("Action completed");
    }
}

ECSフォーマットが有効な場合、このログはタイムスタンプ、レベル、メッセージ、クラス名、スレッド、アプリケーションメタデータを含むJSONオブジェクトになります。この構造により、モニタリングプラットフォームでの検索と集計が簡素化されます。

グレースフルシャットダウンがデフォルトで有効に

重要な変更:グレースフルシャットダウンがデフォルトで有効になりました。処理中のHTTPリクエストはサーバーのシャットダウン前に完了するため、デプロイ時の502エラーを防止できます。

properties
# application.properties
# Graceful shutdown is now ON by default
# To restore previous behavior (immediate shutdown):
server.shutdown=immediate

# Configure maximum wait timeout (30s default)
spring.lifecycle.timeout-per-shutdown-phase=45s

この動作はすべての組み込みサーバーに適用されます:Tomcat、Jetty、Undertow、Reactor Netty。

LifecycleConfig.javajava
@Configuration
public class LifecycleConfig {

    private static final Logger logger = LoggerFactory.getLogger(LifecycleConfig.class);

    @Bean
    public ApplicationListener<ContextClosedEvent> gracefulShutdownListener() {
        // This listener executes at shutdown start
        return event -> {
            logger.info("Graceful shutdown initiated - completing in-flight requests");
            // Custom logic: close connections, save state, etc.
        };
    }
}

この改善により、ほとんどの場合、追加設定なしでゼロダウンタイムデプロイの実装が簡素化されます。

バーチャルスレッドサポートの拡張

Spring Boot 3.4は、バーチャルスレッドサポート(Java 21以上)をより多くのコンポーネントに拡張しています。OtlpMeterRegistryとUndertowサーバーが、有効化時にバーチャルスレッドを使用するようになりました。

properties
# application.properties
# Enable virtual threads globally
spring.threads.virtual.enabled=true

この単一のプロパティにより、アプリケーションのスレッディングモデルが変換されます。各HTTPリクエストが独自のバーチャルスレッドを取得し、スレッドプールを枯渇させることなく数千の同時接続を可能にします。

AsyncService.javajava
@Service
public class AsyncService {

    private static final Logger logger = LoggerFactory.getLogger(AsyncService.class);

    // With virtual threads enabled, each blocking call
    // no longer consumes an OS thread
    public String fetchExternalData() {
        logger.info("Executing on thread: {}", Thread.currentThread());

        // Blocking HTTP call - with virtual threads,
        // the OS thread is released during I/O wait
        return restClient.get()
            .uri("https://api.external.com/data")
            .retrieve()
            .body(String.class);
    }
}

バーチャルスレッドは、I/O負荷の高いアプリケーションで真価を発揮します:HTTP呼び出し、データベースクエリ、ファイル操作。OSスレッドは待機時間中に解放され、再開時に再割り当てされます。

互換性

バーチャルスレッドにはJava 21以上が必要です。Java 17では、このプロパティは無視され、従来の動作が適用されます。

改良されたRestClientとRestTemplate

Spring Boot 3.4は、HTTPクライアントの設定を標準化しています。HttpRequestFactoryの選択は、クラスパスに基づく明確な優先順位に従うようになりました。

HttpClientConfig.javajava
@Configuration
public class HttpClientConfig {

    @Bean
    public RestClient restClient(RestClient.Builder builder) {
        // Spring Boot automatically chooses implementation:
        // 1. Apache HTTP Components (if present)
        // 2. Jetty Client
        // 3. Reactor Netty
        // 4. JDK HttpClient (Java 11+)
        // 5. SimpleClientHttpRequestFactory (fallback)
        return builder
            .baseUrl("https://api.example.com")
            .defaultHeader("Accept", "application/json")
            .build();
    }
}

リダイレクト動作の設定も簡素化されています。

properties
# application.properties
# Force specific implementation
spring.http.client.factory=jdk

# Configure redirect behavior
spring.http.client.redirects=dont-follow

より詳細な制御には、新しいClientHttpRequestFactoryBuilderによる完全なプログラマティック設定が可能です。

CustomHttpClientConfig.javajava
@Configuration
public class CustomHttpClientConfig {

    @Bean
    public RestClient customRestClient(ClientHttpRequestFactoryBuilder factoryBuilder) {
        // Advanced factory configuration
        ClientHttpRequestFactory factory = factoryBuilder
            .httpComponents()
            .connectTimeout(Duration.ofSeconds(5))
            .readTimeout(Duration.ofSeconds(30))
            .build();

        return RestClient.builder()
            .requestFactory(factory)
            .baseUrl("https://api.example.com")
            .build();
    }
}

このアプローチは、基盤となるHTTP実装に関係なく、一貫したAPIで最大限の柔軟性を提供します。

Spring Bootの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

流暢なテストのためのMockMvcTester

Spring Boot 3.4は、MockMvcのAssertJベースの代替であるMockMvcTesterを導入しています。この新しいアプローチにより、テストがより読みやすく表現力豊かになります。

UserControllerTest.javajava
@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvcTester mockMvc; // New class!

    @Test
    void shouldReturnUserById() {
        // Fluent API with AssertJ
        mockMvc.get().uri("/api/users/{id}", 1)
            .assertThat()
            .hasStatusOk()
            .hasContentType(MediaType.APPLICATION_JSON)
            .bodyJson()
            .extractingPath("$.name")
            .isEqualTo("John Doe");
    }

    @Test
    void shouldReturn404ForUnknownUser() {
        mockMvc.get().uri("/api/users/{id}", 999)
            .assertThat()
            .hasStatus(HttpStatus.NOT_FOUND)
            .bodyJson()
            .extractingPath("$.error")
            .isEqualTo("User not found");
    }
}

従来のMockMvcを使用したアプローチと比較して、アサーションがより簡潔になり、チェーンがより自然に感じられます。

ComparisonTest.javajava
@WebMvcTest(UserController.class)
class ComparisonTest {

    @Autowired
    private MockMvcTester mockMvcTester;

    @Autowired
    private MockMvc mockMvc;

    @Test
    void withMockMvcTester() {
        // New approach: fluent and concise
        mockMvcTester.post().uri("/api/users")
            .contentType(MediaType.APPLICATION_JSON)
            .content("{\"name\": \"Jane\"}")
            .assertThat()
            .hasStatus(HttpStatus.CREATED)
            .hasHeader("Location", "/api/users/2");
    }

    @Test
    void withClassicMockMvc() throws Exception {
        // Old approach: more verbose
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"name\": \"Jane\"}"))
            .andExpect(status().isCreated())
            .andExpect(header().string("Location", "/api/users/2"));
    }
}

MockMvcTesterは、クラスパスにAssertJが存在する場合に自動設定されます(spring-boot-starter-testにデフォルトで含まれています)。

Docker ComposeとTestcontainersの改良

Docker Composeサポートは、複数の設定ファイルとカスタム引数による柔軟性を獲得しています。

properties
# application.properties
# Use multiple Docker Compose files
spring.docker.compose.file=compose.yaml,compose-dev.yaml

# Pass arguments at startup
spring.docker.compose.start.arguments=--scale redis=2

# Arguments at shutdown
spring.docker.compose.stop.arguments=--timeout 60

新しいサービスは自動的に検出および設定されます。

yaml
# compose.yaml
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    ports:
      - "5432:5432"

  redis-stack:
    image: redis/redis-stack:latest
    ports:
      - "6379:6379"
      - "8001:8001" # RedisInsight UI

  grafana-lgtm:
    image: grafana/otel-lgtm:latest
    ports:
      - "3000:3000"  # Grafana
      - "4317:4317"  # OTLP gRPC

Spring Boot 3.4はこれらのサービスを自動検出し、対応する接続プロパティを設定します。

Testcontainersでのテストでは、新しいコンテナがサポートされています。

IntegrationTestConfig.javajava
@TestConfiguration(proxyBeanMethods = false)
public class IntegrationTestConfig {

    @Bean
    @ServiceConnection
    public PostgreSQLContainer<?> postgresContainer() {
        return new PostgreSQLContainer<>("postgres:16");
    }

    @Bean
    @ServiceConnection
    public RedisStackContainer redisStackContainer() {
        // New Redis Stack support
        return new RedisStackContainer("redis/redis-stack:latest");
    }

    @Bean
    @ServiceConnection
    public LgtmStackContainer observabilityContainer() {
        // New Grafana LGTM support (Loki, Grafana, Tempo, Mimir)
        return new LgtmStackContainer("grafana/otel-lgtm:latest");
    }
}

@ServiceConnectionアノテーションは接続プロパティを自動的に設定し、ほとんどの場合で@DynamicPropertySourceの必要性を排除します。

Actuator SSLと可観測性

新しい/actuator/infoエンドポイントは、設定済みのSSL証明書に関する情報を公開するようになりました:有効期限、発行者、サブジェクト。

properties
# application.properties
# Enable SSL information in actuator
management.info.ssl.enabled=true

# Configure warning threshold for expiring certificates
management.health.ssl.certificate-validity-warning-threshold=30d

この機能により、actuatorを通じて直接証明書の有効期限を監視でき、更新の自動化が容易になります。

SslMonitoringConfig.javajava
@Configuration
public class SslMonitoringConfig {

    @Bean
    public HealthIndicator sslCertificateHealth(SslInfo sslInfo) {
        return () -> {
            // Custom certificate validity check
            boolean allValid = sslInfo.getBundles().values().stream()
                .flatMap(bundle -> bundle.getCertificates().stream())
                .allMatch(cert -> cert.getValidityEnds().isAfter(Instant.now()));

            return allValid
                ? Health.up().build()
                : Health.down().withDetail("reason", "Certificate expiring soon").build();
        };
    }
}

可観測性については、OTLPトランスポートがHTTPに加えてgRPCをサポートするようになりました。

properties
# application.properties
# Use gRPC for OTLP (traces and metrics)
management.otlp.tracing.transport=grpc
management.otlp.tracing.endpoint=http://localhost:4317

# New: group applications together
spring.application.group=payment-services

アプリケーショングループ(spring.application.group)により、可観測性ダッシュボードで複数のサービスを論理的にグループ化できます。

より軽量なOCIイメージ

デフォルトのOCIイメージビルダーがpaketobuildpacks/builder-jammy-baseからpaketobuildpacks/builder-jammy-java-tinyに変更され、大幅に小さなイメージが生成されます。

build.gradlegroovy
tasks.named("bootBuildImage") {
    // Native ARM support (new)
    imagePlatform = "linux/arm64"

    // New security flag
    trustBuilder = false

    // Image configuration
    imageName = "myregistry.com/myapp:${version}"
}

新しいimagePlatformパラメータにより、ARMおよびx64のクロスプラットフォームビルドが簡素化されます。

xml
<!-- pom.xml -->
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <image>
            <!-- Native ARM support -->
            <platform>linux/arm64</platform>
            <!-- Builder optimized for minimal images -->
            <builder>paketobuildpacks/builder-jammy-java-tiny</builder>
        </image>
    </configuration>
</plugin>

これらの最適化されたイメージは、起動が速く、リソース消費が少なく、特にKubernetesデプロイメントに有利です。

移行に関する注意

ビルダーの変更は、特定のシステムツールに依存するアプリケーションに影響を与える可能性があります。本番環境にデプロイする前に新しいイメージをテストすることを推奨します。

Bean Validationの変更

Spring Boot 3.4は、バリデーション動作をBean Validation仕様に合わせています。バリデーションはネストされたプロパティに自動的にカスケードされなくなりました。

ConfigProperties.javajava
@ConfigurationProperties(prefix = "app")
@Validated
public class AppConfig {

    @NotBlank
    private String name;

    // IMPORTANT: @Valid required to cascade validation
    @Valid
    private DatabaseConfig database;

    // Without @Valid, ServerConfig constraints will NOT be checked
    private ServerConfig server;

    // Getters and setters
}

public class DatabaseConfig {
    @NotBlank
    private String url;

    @Min(1)
    private int poolSize;

    // Getters and setters
}

public class ServerConfig {
    @NotNull // This constraint will NOT be checked without @Valid on parent
    private Integer port;

    // Getters and setters
}

この変更は既存のアプリケーションに影響を与える可能性があります。@ConfigurationPropertiesクラスを確認し、バリデーションがカスケードされるべき箇所に@Validを追加する必要があります。

@MockBeanと@SpyBeanの非推奨化

Spring Bootの@MockBean@SpyBeanアノテーションは、新しいMockitoアノテーションに置き換えられ、非推奨となりました。

UserServiceTest.java (new approach)java
@SpringBootTest
class UserServiceTest {

    // New: native Mockito annotations
    @MockitoBean
    private UserRepository userRepository;

    @MockitoSpyBean
    private EmailService emailService;

    @Autowired
    private UserService userService;

    @Test
    void shouldCreateUser() {
        // Mock configuration
        when(userRepository.save(any())).thenReturn(new User(1L, "test@example.com"));

        userService.createUser("test@example.com");

        // Spy verification
        verify(emailService).sendWelcomeEmail("test@example.com");
    }
}

旧アノテーションは引き続き動作しますが、非推奨の警告が表示されます。@MockitoBean@MockitoSpyBeanへの移行を計画することを推奨します。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

まとめ

Spring Boot 3.4は、生産性と可観測性において大幅な改善を提供しています:

移行チェックリスト:

  • 構造化ロギングを有効化して可観測性を向上させる
  • グレースフルシャットダウンの動作を確認する(デフォルトで有効化済み)
  • I/O負荷の高いアプリケーションにバーチャルスレッドを検討する(Java 21以上)
  • より読みやすいテストのためにMockMvcTesterに移行する
  • ネストされた@ConfigurationPropertiesプロパティに@Validを追加する
  • @MockBean/@SpyBean@MockitoBean/@MockitoSpyBeanに置き換える
  • 本番デプロイ前に新しいOCIイメージをテストする

Spring Boot 3.4は、可観測性基準とパフォーマンスに特に注力し、モダンJava開発のリファレンスフレームワークとしての地位を強化しています。

参考資料:

タグ

#spring boot
#java
#spring boot 3.4
#spring framework
#backend

共有

関連記事