Spring Boot 3.4: Tất cả tính năng mới được giải thích chi tiết

Spring Boot 3.4 mang đến structured logging native, virtual threads mở rộng, graceful shutdown mặc định và MockMvcTester. Hướng dẫn đầy đủ các tính năng mới.

Tính năng mới và cải tiến của Spring Boot 3.4

Spring Boot 3.4, phát hành vào tháng 11 năm 2024, mang đến những cải tiến đáng kể về năng suất lập trình viên và hiệu năng ứng dụng. Phiên bản này giới thiệu structured logging native, kích hoạt graceful shutdown theo mặc định và mở rộng hỗ trợ virtual threads trên toàn bộ framework.

Điều kiện tiên quyết

Spring Boot 3.4 yêu cầu tối thiểu Java 17 và hỗ trợ Java 21 cho virtual threads. Phiên bản này sử dụng Spring Framework 6.2.

Structured logging native

Structured logging đại diện cho một bước tiến quan trọng trong khả năng quan sát ứng dụng. Thay vì các log dạng văn bản khó phân tích, Spring Boot 3.4 tạo ra log ở định dạng JSON có thể được xử lý bởi các công cụ như Elasticsearch, Grafana Loki hoặc Datadog.

Ba định dạng được hỗ trợ native: Elastic Common Schema (ECS), Logstash và 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

Cấu hình đơn giản này tự động tạo ra các log JSON có cấu trúc.

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");
    }
}

Với định dạng ECS được kích hoạt, log này trở thành một đối tượng JSON chứa timestamp, mức độ, thông điệp, tên lớp, thread và metadata ứng dụng. Cấu trúc này đơn giản hóa việc tìm kiếm và tổng hợp trên các nền tảng giám sát.

Graceful Shutdown được kích hoạt mặc định

Thay đổi quan trọng: graceful shutdown giờ đây được kích hoạt mặc định. Các yêu cầu HTTP đang xử lý được hoàn thành trước khi server tắt, ngăn chặn lỗi 502 trong quá trình triển khai.

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

Hành vi này áp dụng cho tất cả các embedded server: Tomcat, Jetty, Undertow và 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.
        };
    }
}

Cải tiến này đơn giản hóa việc triển khai không có thời gian ngừng hoạt động, không cần cấu hình bổ sung trong hầu hết các trường hợp.

Hỗ trợ Virtual Threads mở rộng

Spring Boot 3.4 mở rộng hỗ trợ virtual threads (Java 21+) cho nhiều thành phần hơn. OtlpMeterRegistry và server Undertow giờ đây sử dụng virtual threads khi được kích hoạt.

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

Property duy nhất này biến đổi mô hình threading của ứng dụng. Mỗi yêu cầu HTTP nhận được virtual thread riêng, cho phép hàng nghìn kết nối đồng thời mà không cạn kiệt thread pool.

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);
    }
}

Virtual threads phát huy sức mạnh trong các ứng dụng nặng I/O: gọi HTTP, truy vấn cơ sở dữ liệu, thao tác với tệp. Thread hệ điều hành được giải phóng trong thời gian chờ và được gán lại khi tiếp tục.

Tương thích

Virtual threads yêu cầu tối thiểu Java 21. Trên Java 17, property này bị bỏ qua và hành vi cổ điển được áp dụng.

RestClient và RestTemplate cải tiến

Spring Boot 3.4 chuẩn hóa cấu hình HTTP client. Việc chọn HttpRequestFactory giờ đây tuân theo thứ tự ưu tiên rõ ràng dựa trên classpath.

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();
    }
}

Cấu hình hành vi chuyển hướng cũng được đơn giản hóa.

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

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

Để kiểm soát chi tiết hơn, ClientHttpRequestFactoryBuilder mới cho phép cấu hình lập trình đầy đủ.

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();
    }
}

Cách tiếp cận này cung cấp sự linh hoạt tối đa với API nhất quán bất kể triển khai HTTP bên dưới.

Sẵn sàng chinh phục phỏng vấn Spring Boot?

Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.

MockMvcTester cho kiểm thử mượt mà

Spring Boot 3.4 giới thiệu MockMvcTester, một giải pháp thay thế dựa trên AssertJ cho MockMvc. Cách tiếp cận mới này làm cho các test dễ đọc và biểu đạt hơn.

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");
    }
}

So với cách tiếp cận cũ sử dụng MockMvc, các assertion ngắn gọn hơn và chuỗi phương thức tự nhiên hơn.

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 được tự động cấu hình khi AssertJ có mặt trong classpath (được bao gồm mặc định với spring-boot-starter-test).

Docker Compose và Testcontainers cải tiến

Hỗ trợ Docker Compose linh hoạt hơn với nhiều tệp cấu hình và các tham số tùy chỉnh.

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

Các service mới được tự động phát hiện và cấu hình.

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 tự động phát hiện các service này và cấu hình các thuộc tính kết nối tương ứng.

Đối với kiểm thử với Testcontainers, các container mới được hỗ trợ.

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");
    }
}

Annotation @ServiceConnection tự động cấu hình các thuộc tính kết nối, loại bỏ nhu cầu sử dụng @DynamicPropertySource trong hầu hết các trường hợp.

Actuator SSL và khả năng quan sát

Endpoint /actuator/info mới giờ đây hiển thị thông tin về các chứng chỉ SSL đã cấu hình: ngày hết hạn, nhà phát hành và chủ thể.

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

Tính năng này cho phép giám sát hết hạn chứng chỉ trực tiếp qua actuator, tạo điều kiện tự động hóa việc gia hạn.

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();
        };
    }
}

Về khả năng quan sát, truyền tải OTLP giờ đây hỗ trợ gRPC ngoài HTTP.

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

Nhóm ứng dụng (spring.application.group) cho phép nhóm logic nhiều service trong dashboard quan sát.

Image OCI nhẹ hơn

Builder image OCI mặc định thay đổi từ paketobuildpacks/builder-jammy-base sang paketobuildpacks/builder-jammy-java-tiny, tạo ra image nhỏ hơn đáng kể.

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

    // New security flag
    trustBuilder = false

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

Tham số imagePlatform mới đơn giản hóa việc xây dựng đa nền tảng cho ARM và 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>

Các image được tối ưu hóa này khởi động nhanh hơn và tiêu thụ ít tài nguyên hơn, đặc biệt có lợi cho triển khai trên Kubernetes.

Lưu ý di chuyển

Việc thay đổi builder có thể ảnh hưởng đến các ứng dụng phụ thuộc vào một số công cụ hệ thống nhất định. Nên kiểm thử image mới trước khi triển khai lên production.

Thay đổi Bean Validation

Spring Boot 3.4 điều chỉnh hành vi validation phù hợp với đặc tả Bean Validation. Validation không còn tự động lan truyền đến các thuộc tính lồng nhau.

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
}

Thay đổi này có thể ảnh hưởng đến các ứng dụng hiện có. Cần kiểm tra các lớp @ConfigurationProperties và thêm @Valid ở nơi validation cần lan truyền.

Ngừng sử dụng @MockBean và @SpyBean

Các annotation @MockBean@SpyBean của Spring Boot đã bị đánh dấu ngừng sử dụng, thay thế bằng các annotation mới của 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");
    }
}

Các annotation cũ vẫn hoạt động nhưng hiển thị cảnh báo ngừng sử dụng. Nên lên kế hoạch di chuyển sang @MockitoBean@MockitoSpyBean.

Bắt đầu luyện tập!

Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

Kết luận

Spring Boot 3.4 mang đến những cải tiến đáng kể về năng suất và khả năng quan sát:

Danh sách kiểm tra di chuyển:

  • Kích hoạt structured logging để cải thiện khả năng quan sát
  • Xác minh hành vi graceful shutdown (giờ đây được kích hoạt mặc định)
  • Cân nhắc virtual threads cho các ứng dụng nặng I/O (Java 21+)
  • Di chuyển sang MockMvcTester cho các test dễ đọc hơn
  • Thêm @Valid vào các thuộc tính @ConfigurationProperties lồng nhau
  • Thay thế @MockBean/@SpyBean bằng @MockitoBean/@MockitoSpyBean
  • Kiểm thử image OCI mới trước khi triển khai lên production

Spring Boot 3.4 củng cố vị thế là framework tham chiếu cho phát triển Java hiện đại, với sự quan tâm đặc biệt đến các tiêu chuẩn quan sát và hiệu năng.

Nguồn tham khảo:

Thẻ

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

Chia sẻ

Bài viết liên quan