Spring Boot 3 - erfolgreich migrieren

09.05.2023 | Matthias

Spring Boot ist eine beliebte Basis, auf welcher Java-Services effizient entwickelt werden können. Kürzlich ist das Framework in der Version 3 erschienen - wir fassen die wichtigsten Erfahrungen, welche wir beim Update unserer Java-Applikationen gemacht haben, kurz zusammen.

Die wichtigsten Neuerungen in Spring Boot 3 sind:

  • Java 17 als Basis
  • Migration von JavaEE auf JakartaEE: javax.* durch jakarta.* ersetzt
  • Verbesserter Support für Observability mit Micrometer
  • GraalVM Native Image Support
  • Aktualisierung der von Spring Boot verwalteten Dependencies
  • Anpassungen am Autoconfiguration-Mechanismus

Spring Boot 3

Spring Boot 3 wird - je nach verwendetem Build-Tool - unterschiedlich als Dependency eingebunden. Mit Maven zum Beispiel als Parent-Projekt:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.6</version>
    <relativePath />
</parent>

Die Major-Version 3 bringt viele Dependency-Updates mit, diese sind je nach Projekt unterschiedlich relevant. Zu erwähnen ist auch, dass Spring Cloud ab der Version 2022.0.x Spring Boot 3 unterstützt.

Spring Boot 3 setzt Java 17 voraus

Spring Boot 3 setzt Java 17 voraus - falls sich hier etwas Lifecycle-Aufwand angestaut hat, ist jetzt ein guter Zeitpunkt, um diesen zu abzubauen…

Mit Maven wird zum Beispiel folgendes Property genutzt, um die Java-Version zu definieren:

<properties>
    <java.version>17</java.version>
</properties>

javax wird jakarta

Dies ist die erste Version des Frameworks, welche die Jakarta EE 9 APIs (jakarta.*) und nicht EE 8 (javax.*) verwendet.

Das heisst, dass viele Imports angepasst werden müssten - für kleinere Projekte dürfte ein simples Search/Replace genügen, für grössere Projekte können zum Beispiel die Entwicklungsumgebung oder OpenRewrite Recipes. dabei helfen.

// zum Beispiel:
import javax.servlet.*;

// wird neu:
import jakarta.servlet.*;

Etwas weniger offensichtlich ist dabei, dass auch sämtliche Dependencies die neuen jakarta.*-Namespaces verwenden müssen. Dependencies, welche nicht von Spring Boot verwaltet werden, müssen also ebenfalls aktualisiert werden.

Spring Security 6

Bereits in Spring Boot 2.7 wurde die Klasse WebSecurityConfigurerAdapter als deprecated markiert - mit Spring Boot 3 wird diese definitiv entfernt. Damit muss die Security-Konfiguration angepasst werden - neu dient dazu ein Bean vom Typ SecurityFilterChain.

@Configuration
public class PublicSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) {
        // ...
    }
}

// ... wird neu zu ...

@Configuration
public class PublicSecurityConfig {
    @Bean
    public SecurityFilterChain gatesSecurityFilterChain(HttpSecurity http) {
        http
            .authorizeHttpRequests()
            .requestMatchers(GET, "/api/public-resource/**").permitAll();
        return http.build();
    }
}

Die Annotation EnableGlobalMethodSecurity wurde ebenfalls entfernt - neu wird EnableMethodSecurity verwendet:

// Bisher:
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true) // jsr250Enabled: @RolesAllowed

// Neu:
@EnableMethodSecurity(jsr250Enabled = true)

Für eine komplette Übersicht zu den Anpassungen in Spring Security 6 siehe: Spring Security 6 Migration Guide.

Anpassungen an Metrics und Observability

Spring 6 hat Spring Observability eingeführt, welches auf Micrometer und Micrometer Tracing aufbaut. Es kann verschiedene Anwendungsmetriken mit Micrometer aufzeichnen und bietet Tracing-Unterstützung mit OpenZipkin oder OpenTelemetry.

Die wichtigste Anpassung im Bereich Metriken und Tracing ist wohl der Ersatz von Sleuth durch Micrometer Tracing. Damit ändert sich auch die API, mit welcher Spans erstellt werden:

// Bisher;
import brave.Span;
import brave.Tracer;

Span span = tracer.nextSpan().name(spanName).start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)){
    // ...
} finally {
    span.finish();
}

// Neu:
import io.micrometer.tracing.ScopedSpan;
import io.micrometer.tracing.Tracer;

ScopedSpan span = tracer.startScopedSpan(spanName);
try {
    // ...
} finally {
    span.end();
}

Auch die Properties, mittels welchen Metriken an einen Exporter übergeben werden, haben sich geändert:

# Bisher:
management.metrics.export.<product>
# zum Beispiel:
management.metrics.export.stackdriver 

# Neu:
management.<product>.metrics.export
# zum Beispiel:
management.stackdriver.metrics.export

Wenn Integrations-Tests für Metriken geschrieben wurden, muss die Annotation angepasst werden, welche die Metriken in Tests aktiviert:

// Bisher:
@AutoConfigureMetrics

// Neu:
@AutoConfigureObservability

Anpassungen an der Autoconfiguration

Wenn eigene Starter-Dependencies erstellt wurden, welche Autokonfigurationen enthalten, müssen diese angepasst werden. Bisher wurden solche Konfiguration in einer Datei namens META-INF/spring.factories mittels ...EnableAutoConfiguration definiert, neu werden sie in ...AutoConfiguration.imports referenziert:

# Bisher:
# File META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    ch.avega.blog.ExampleConfiguration

# Neu:
# File META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
ch.avega.blog.ExampleConfiguration

Fazit

Wenn du eine komplette Migrations-Anleitung suchst, hilft dir vielleicht der Spring Boot 3.0 Migration Guide weiter.

Falls du Feedback zu diesem Blogpost hast, Unterstützung benötigst oder Fragen hast freue ich mich von dir zu hören: matthias.fritschi@avega.ch.