Spring Boot 3.4: Semua fitur baru dijelaskan lengkap

Spring Boot 3.4 menghadirkan structured logging native, virtual threads yang diperluas, graceful shutdown default, dan MockMvcTester. Panduan lengkap fitur-fitur baru.

Fitur baru dan peningkatan Spring Boot 3.4

Spring Boot 3.4, dirilis pada November 2024, menghadirkan peningkatan signifikan untuk produktivitas developer dan performa aplikasi. Versi ini memperkenalkan structured logging native, mengaktifkan graceful shutdown secara default, dan memperluas dukungan virtual threads di seluruh framework.

Prasyarat

Spring Boot 3.4 memerlukan minimal Java 17 dan mendukung Java 21 untuk virtual threads. Versi ini menggunakan Spring Framework 6.2.

Structured logging native

Structured logging merupakan kemajuan penting untuk observabilitas aplikasi. Alih-alih log berbasis teks yang sulit dianalisis, Spring Boot 3.4 menghasilkan log dalam format JSON yang dapat dikonsumsi oleh tools seperti Elasticsearch, Grafana Loki, atau Datadog.

Tiga format didukung secara native: Elastic Common Schema (ECS), Logstash, dan 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

Konfigurasi sederhana ini secara otomatis menghasilkan log JSON terstruktur yang terformat.

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

Dengan format ECS diaktifkan, log ini menjadi objek JSON yang berisi timestamp, level, pesan, nama kelas, thread, dan metadata aplikasi. Struktur ini menyederhanakan pencarian dan agregasi di platform monitoring.

Graceful Shutdown aktif secara default

Perubahan penting: graceful shutdown kini aktif secara default. Request HTTP yang sedang berjalan diproses sebelum server dimatikan, mencegah error 502 selama deployment.

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

Perilaku ini berlaku untuk semua embedded server: Tomcat, Jetty, Undertow, dan 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.
        };
    }
}

Peningkatan ini menyederhanakan implementasi deployment tanpa downtime, tanpa perlu konfigurasi tambahan di sebagian besar kasus.

Dukungan Virtual Threads yang diperluas

Spring Boot 3.4 memperluas dukungan virtual threads (Java 21+) ke lebih banyak komponen. OtlpMeterRegistry dan server Undertow kini menggunakan virtual threads saat diaktifkan.

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

Property tunggal ini mengubah model threading aplikasi. Setiap request HTTP mendapat virtual thread-nya sendiri, memungkinkan ribuan koneksi bersamaan tanpa menghabiskan 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 unggul pada aplikasi dengan beban I/O tinggi: panggilan HTTP, query database, operasi file. Thread OS dilepaskan selama waktu tunggu dan dialokasikan ulang saat dilanjutkan.

Kompatibilitas

Virtual threads memerlukan minimal Java 21. Pada Java 17, property ini diabaikan dan perilaku klasik yang berlaku.

RestClient dan RestTemplate yang ditingkatkan

Spring Boot 3.4 menormalisasi konfigurasi HTTP client. Pemilihan HttpRequestFactory kini mengikuti urutan prioritas yang jelas berdasarkan 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();
    }
}

Konfigurasi perilaku redirect juga disederhanakan.

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

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

Untuk kontrol lebih detail, ClientHttpRequestFactoryBuilder yang baru memungkinkan konfigurasi programatik penuh.

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

Pendekatan ini menawarkan fleksibilitas maksimal dengan API yang konsisten terlepas dari implementasi HTTP yang mendasarinya.

Siap menguasai wawancara Spring Boot Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

MockMvcTester untuk pengujian yang lebih lancar

Spring Boot 3.4 memperkenalkan MockMvcTester, alternatif berbasis AssertJ untuk MockMvc. Pendekatan baru ini membuat test lebih mudah dibaca dan ekspresif.

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

Dibandingkan dengan pendekatan lama menggunakan MockMvc, assertion lebih ringkas dan chaining terasa lebih natural.

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 dikonfigurasi secara otomatis ketika AssertJ ada di classpath (sudah termasuk secara default dengan spring-boot-starter-test).

Docker Compose dan Testcontainers yang ditingkatkan

Dukungan Docker Compose mendapat fleksibilitas lebih dengan multiple file konfigurasi dan argumen kustom.

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

Service baru secara otomatis terdeteksi dan terkonfigurasi.

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 secara otomatis mendeteksi service-service ini dan mengkonfigurasi property koneksi yang sesuai.

Untuk pengujian dengan Testcontainers, container baru didukung.

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

Anotasi @ServiceConnection secara otomatis mengkonfigurasi property koneksi, menghilangkan kebutuhan @DynamicPropertySource di sebagian besar kasus.

Actuator SSL dan observabilitas

Endpoint /actuator/info yang baru kini mengekspos informasi tentang sertifikat SSL yang dikonfigurasi: tanggal validitas, penerbit, dan subjek.

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

Fitur ini memungkinkan pemantauan masa berlaku sertifikat langsung melalui actuator, memudahkan otomatisasi pembaruan.

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

Untuk observabilitas, transport OTLP kini mendukung gRPC selain 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

Grup aplikasi (spring.application.group) memungkinkan pengelompokan logis beberapa service di dashboard observabilitas.

Image OCI yang lebih ringan

Builder image OCI default berubah dari paketobuildpacks/builder-jammy-base ke paketobuildpacks/builder-jammy-java-tiny, menghasilkan image yang jauh lebih kecil.

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

    // New security flag
    trustBuilder = false

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

Parameter imagePlatform yang baru menyederhanakan build cross-platform untuk ARM dan 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>

Image yang dioptimalkan ini lebih cepat dimulai dan mengonsumsi lebih sedikit resource, terutama menguntungkan untuk deployment di Kubernetes.

Catatan Migrasi

Perubahan builder dapat mempengaruhi aplikasi yang bergantung pada tool sistem tertentu. Uji image baru sebelum di-deploy ke production.

Perubahan Bean Validation

Spring Boot 3.4 menyelaraskan perilaku validasi dengan spesifikasi Bean Validation. Validasi tidak lagi otomatis menyebar ke property yang bersarang.

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
}

Perubahan ini dapat mempengaruhi aplikasi yang sudah ada. Periksa kelas @ConfigurationProperties dan tambahkan @Valid di mana validasi harus menyebar.

Deprecation @MockBean dan @SpyBean

Anotasi @MockBean dan @SpyBean dari Spring Boot sudah di-deprecate demi anotasi Mockito yang baru.

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

Anotasi lama masih berfungsi tetapi menampilkan peringatan deprecation. Rencanakan migrasi ke @MockitoBean dan @MockitoSpyBean.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Kesimpulan

Spring Boot 3.4 memberikan peningkatan substansial dalam produktivitas dan observabilitas:

Checklist migrasi:

  • Aktifkan structured logging untuk observabilitas yang lebih baik
  • Verifikasi perilaku graceful shutdown (sekarang aktif secara default)
  • Pertimbangkan virtual threads untuk aplikasi dengan beban I/O tinggi (Java 21+)
  • Migrasi ke MockMvcTester untuk test yang lebih mudah dibaca
  • Tambahkan @Valid pada property @ConfigurationProperties yang bersarang
  • Ganti @MockBean/@SpyBean dengan @MockitoBean/@MockitoSpyBean
  • Uji image OCI baru sebelum deployment ke production

Spring Boot 3.4 memperkuat posisinya sebagai framework referensi untuk pengembangan Java modern, dengan perhatian khusus pada standar observabilitas dan performa.

Sumber:

Tag

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

Bagikan

Artikel terkait