Spring Boot 2.0 – co się zmieniło

TL;DR: Spring Boot 2.0 to major release z breaking changes: wymagana Java 8+, wprowadzenie reactive programming (Spring WebFlux), nowe actuator endpoints, przepisane security auto-configuration i znacznie ulepszone configuration properties binding.

Dlaczego Spring Boot 2.0 to przełomowy moment?

Spring Boot 2.0 to nie zwykła aktualizacja – to kompletna modernizacja platformy. Podobnie jak przejście z Java 7 na 8 zmieniło sposób pisania kodu, Spring Boot 2.0 wprowadza reactive programming, lepsze performance i nowoczesne podejście do configuration. To inwestycja w przyszłość Java ecosystem na następne 5 lat.

Spring Boot 2.0 bazuje na Spring Framework 5.0 i wprowadza wsparcie dla reactive programming, które stanie się kluczowe dla high-performance applications.

Co się nauczysz:

  • Breaking changes i wymagania migracji z Boot 1.x
  • Reactive programming z Spring WebFlux
  • Nowe Actuator endpoints i metryki
  • Zmiany w Spring Security auto-configuration
  • Ulepszone configuration properties i validation
Wymagania wstępne: Doświadczenie z Spring Boot 1.x, znajomość Spring Framework basics, podstawy reactive programming będą pomocne ale nie wymagane.

Kluczowe breaking changes

Java 8 jako minimum requirement

Spring Boot 2.0 wymaga minimum Java 8, co pozwala na pełne wykorzystanie lambda expressions, Optional, Stream API i innych Java 8 features w całym framework.



    1.8
    2.0.0.M7



    org.springframework.boot
    spring-boot-starter-parent
    2.0.0.M7
    

Breaking change: Aplikacje Spring Boot 1.x działające na Java 7 wymagają upgrade do Java 8+ przed migracją na Boot 2.0.

Nowa architektura dependency management

KomponentSpring Boot 1.5Spring Boot 2.0Impact
Spring Framework4.3.x5.0.xReactive support, Java 8 APIs
Hibernate5.0.x5.2.xJPA 2.1 improvements
Jackson2.8.x2.9.xJava 8 time support
Tomcat8.5.x8.5.x/9.0.xHTTP/2 support

Reactive Programming z Spring WebFlux

Największa nowość w Spring Boot 2.0 to pełne wsparcie dla reactive programming przez Spring WebFlux – alternatywę dla tradycyjnego Spring MVC.

WebFlux vs MVC – kiedy używać czego?

// Tradycyjny Spring MVC Controller
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public ResponseEntity getUser(@PathVariable Long id) {
        User user = userService.findById(id); // Blocking call
        return ResponseEntity.ok(user);
    }
    
    @GetMapping
    public List getAllUsers() {
        return userService.findAll(); // Blocking, loads all to memory
    }
}
// Reactive WebFlux Controller  
@RestController
@RequestMapping("/api/reactive/users")
public class ReactiveUserController {
    
    @Autowired
    private ReactiveUserService userService;
    
    @GetMapping("/{id}")
    public Mono> getUser(@PathVariable Long id) {
        return userService.findById(id) // Non-blocking
                .map(ResponseEntity::ok)
                .defaultIfEmpty(ResponseEntity.notFound().build());
    }
    
    @GetMapping
    public Flux getAllUsers() {
        return userService.findAll(); // Streaming, handles backpressure
    }
    
    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux streamUsers() {
        return userService.findAll()
                .delayElements(Duration.ofSeconds(1)); // Server-Sent Events
    }
}

Reactive dependencies



    org.springframework.boot
    spring-boot-starter-webflux




    org.springframework.boot
    spring-boot-starter-data-mongodb-reactive




    org.springframework.boot
    spring-boot-starter-data-redis-reactive

Pro tip: Nie mieszaj spring-boot-starter-web z spring-boot-starter-webflux w jednej aplikacji. Boot 2.0 automatycznie wybiera stos na podstawie dependencies.

Functional routing jako alternatywa

@Configuration
public class RouterConfiguration {
    
    @Bean
    public RouterFunction userRoutes(UserHandler userHandler) {
        return RouterFunctions
                .route(GET("/api/functional/users/{id}"), userHandler::getUser)
                .andRoute(GET("/api/functional/users"), userHandler::getAllUsers)
                .andRoute(POST("/api/functional/users"), userHandler::createUser)
                .andRoute(PUT("/api/functional/users/{id}"), userHandler::updateUser);
    }
}

@Component
public class UserHandler {
    
    @Autowired
    private ReactiveUserService userService;
    
    public Mono getUser(ServerRequest request) {
        Long id = Long.valueOf(request.pathVariable("id"));
        return userService.findById(id)
                .flatMap(user -> ServerResponse.ok().bodyValue(user))
                .switchIfEmpty(ServerResponse.notFound().build());
    }
    
    public Mono getAllUsers(ServerRequest request) {
        return ServerResponse.ok()
                .contentType(MediaType.APPLICATION_JSON)
                .body(userService.findAll(), User.class);
    }
}

Actuator 2.0 – kompletnie przepisany

Nowa architektura endpoints

Actuator został kompletnie przepisany z focus na security, performance i pluggability:

EndpointBoot 1.5 URLBoot 2.0 URLChanges
Health/health/actuator/healthDetailed health indicators
Metrics/metrics/actuator/metricsMicrometer integration
Info/info/actuator/infoGit info auto-configuration
Env/env/actuator/envBetter property masking

Konfiguracja Actuator 2.0

# application.yml - Actuator 2.0 configuration
management:
  endpoints:
    web:
      base-path: /management  # Zmiana base path
      exposure:
        include: health,info,metrics,env  # Explicit exposure
        exclude: shutdown
  endpoint:
    health:
      show-details: when-authorized  # Security-aware
      show-components: always
    metrics:
      enabled: true
  metrics:
    tags:
      application: myapp  # Custom tags
      environment: ${spring.profiles.active}

Micrometer integration

@RestController
public class MetricsController {
    
    private final MeterRegistry meterRegistry;
    private final Counter userCreationCounter;
    private final Timer requestTimer;
    
    public MetricsController(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.userCreationCounter = Counter.builder("user.creation")
                .description("Number of users created")
                .tag("type", "registration")
                .register(meterRegistry);
        this.requestTimer = Timer.builder("http.requests")
                .description("HTTP request duration")
                .register(meterRegistry);
    }
    
    @PostMapping("/users")
    public ResponseEntity createUser(@RequestBody User user) {
        return Timer.Sample.start(meterRegistry)
                .stop(requestTimer, () -> {
                    User savedUser = userService.save(user);
                    userCreationCounter.increment();
                    return ResponseEntity.ok(savedUser);
                });
    }
}
Micrometer to facade dla metrics podobny do SLF4J dla logging. Wspiera Prometheus, Grafana, StatsD, CloudWatch i inne monitoring systems.

Spring Security – nowa auto-configuration

Breaking changes w Security

Spring Boot 2.0 znacznie zmienia domyślną konfigurację security:

// Spring Boot 1.5 - domyślne zachowanie
// Wszystkie endpoints zabezpieczone basic auth
// User: user, password: generowane w logach

// Spring Boot 2.0 - nowe domyślne zachowanie  
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // Actuator endpoints mają osobną konfigurację
        http.authorizeRequests()
                .requestMatchers(EndpointRequest.to(HealthEndpoint.class)).permitAll()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).hasRole("ACTUATOR")
                .anyRequest().authenticated()
                .and()
            .httpBasic();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // Nowy sposób konfiguracji in-memory users
        auth.inMemoryAuthentication()
                .withUser("user")
                .password("{noop}password")  // {noop} = no encoding
                .roles("USER")
                .and()
                .withUser("actuator")
                .password("{noop}actuator")
                .roles("ACTUATOR");
    }
}

OAuth 2.0 i JWT improvements

# application.yml - OAuth 2.0 client configuration
spring:
  security:
    oauth2:
      client:
        registration:
          google:
            client-id: ${GOOGLE_CLIENT_ID}
            client-secret: ${GOOGLE_CLIENT_SECRET}
            scope: openid,profile,email
          github:
            client-id: ${GITHUB_CLIENT_ID}
            client-secret: ${GITHUB_CLIENT_SECRET}
            scope: read:user,user:email
        provider:
          google:
            authorization-uri: https://accounts.google.com/o/oauth2/auth
            token-uri: https://accounts.google.com/o/oauth2/token
            user-info-uri: https://www.googleapis.com/oauth2/v1/userinfo

Configuration Properties overhaul

Relaxed binding 2.0

// Spring Boot 2.0 - ulepszone @ConfigurationProperties
@ConfigurationProperties(prefix = "myapp")
@Validated
public class MyAppProperties {
    
    @NotNull
    @Size(min = 1, max = 100)
    private String applicationName;
    
    @Valid
    private Database database = new Database();
    
    @Valid
    private List<@Valid Server> servers = new ArrayList<>();
    
    // Nested configuration classes
    public static class Database {
        @NotBlank
        private String url;
        
        @Min(1)
        @Max(1000)
        private int maxConnections = 10;
        
        @DurationUnit(ChronoUnit.SECONDS)
        private Duration connectionTimeout = Duration.ofSeconds(30);
        
        // getters/setters
    }
    
    public static class Server {
        @NotBlank
        private String host;
        
        @Min(1024)
        @Max(65535)
        private int port;
        
        // getters/setters
    }
}
# application.yml - Configuration properties
myapp:
  application-name: My Application
  database:
    url: jdbc:postgresql://localhost/mydb
    max-connections: 50
    connection-timeout: 45s  # Duration support
  servers:
    - host: server1.example.com
      port: 8080
    - host: server2.example.com  
      port: 8081
Pro tip: Spring Boot 2.0 ma lepsze error messages dla configuration binding errors i wspiera JSR-303 validation na nested properties.

Migracja z Spring Boot 1.x

Migration checklist

  1. Java 8+ requirement – upgrade JVM version
  2. Dependencies update – check compatibility matrix
  3. Actuator endpoints – update URLs w testach i monitoring
  4. Security configuration – review custom security config
  5. Configuration properties – validate binding changes
  6. Metrics – migrate custom metrics do Micrometer API

Typowe problemy podczas migracji

Pułapka: Actuator endpoints nie są już domyślnie exposed przez web. Musisz explicitly włączyć je w konfiguracji.
// Problem: Spring Boot 1.5 metrics API przestaje działać
// Stary kod
@Autowired
private CounterService counterService;

counterService.increment("user.registration");

// Rozwiązanie: Migracja na Micrometer
@Autowired
private MeterRegistry meterRegistry;

Counter.builder("user.registration")
    .register(meterRegistry)
    .increment();

Performance improvements

Startup time optimization

ObszarImprovementImpact
Bean creationLazy initialization supportFaster startup
Classpath scanningIndexed componentsReduced reflection
Auto-configurationConditional optimizationLess overhead
Reactive stackNon-blocking I/OBetter resource utilization

Memory usage optimizations

# application.yml - Performance tuning
spring:
  main:
    lazy-initialization: true  # Boot 2.2+ feature preview
  jpa:
    hibernate:
      ddl-auto: none  # Don't scan entities on startup
    open-in-view: false  # Disable OSIV for better performance
  jackson:
    serialization:
      write-dates-as-timestamps: false
Czy powinienem natychmiast migrować z Boot 1.5 na 2.0?

Nie od razu. Spring Boot 1.5 będzie wspierane do sierpnia 2019. Zaplanuj migrację w spokojnym tempie, szczególnie jeśli masz dużą aplikację z custom security configuration.

Czy reactive programming zastąpi tradycyjny Spring MVC?

Nie, oba stacki będą współistnieć. Reactive jest lepszy dla high-throughput, I/O-heavy applications. Tradycyjny MVC nadal lepszy dla CRUD applications i zespołów bez reactive experience.

Jak sprawdzić kompatybilność moich dependencies z Boot 2.0?

Użyj Spring Boot’s dependency management i sprawdź release notes. Większość Spring ecosystem libraries ma wersje kompatybilne z Boot 2.0. Sprawdź również migration guide na spring.io.

Czy Actuator 2.0 jest backward compatible?

Nie, URL paths się zmieniły (/health → /actuator/health) i security model jest inny. Musisz update monitoring tools i health check URLs w load balancerach.

Co z aplikacjami używającymi Spring Cloud?

Spring Cloud Finchley będzie pierwszą wersją kompatybilną z Boot 2.0. Obecnie (luty 2017) Spring Cloud działa z Boot 1.5.x. Čekaj na Finchley release train.

🚀 Zadanie dla Ciebie

Eksperymentuj z Spring Boot 2.0 milestone releases:

  1. Utwórz nową aplikację z spring-boot-starter-webflux
  2. Zaimplementuj reactive controller z Mono/Flux endpoints
  3. Skonfiguruj custom Actuator endpoint z Micrometer metrics
  4. Dodaj OAuth 2.0 client configuration dla Google/GitHub
  5. Porównaj performance z equivalent Spring MVC application
  6. Test reactive database operations z reactive MongoDB

Dokumentuj różnice w performance i developer experience między reactive a traditional stack.

Przydatne zasoby:

Planujesz już migrację na Spring Boot 2.0? Które nowe features wydają ci się najbardziej przydatne w twoich projektach?

Zostaw komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Przewijanie do góry