CtrlK
BlogDocsLog inGet started
Tessl Logo

jbaruch/spring-boot-4

Build Spring Boot 4.0 applications - project setup, REST controllers, dependency injection, configuration, actuator, and testing

91

1.79x
Quality

90%

Does it follow best practices?

Impact

97%

1.79x

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

SKILL.mdskills/spring-boot-4/

name:
spring-boot-4
description:
Build applications with Spring Boot 4.0 and Spring Framework 7.0. Use when creating REST APIs, configuring Spring Boot, adding actuator observability, writing tests, or setting up a new Spring Boot 4 project.

Spring Boot 4.0 Skill

Version Matrix

ComponentVersion
Spring Boot4.0.x (GA 2025-11-20, current 4.0.5)
Spring Framework7.0.6+
Java17+ (recommended: latest LTS, first-class support for Java 25)
Jakarta EE11 (Servlet 6.1, Persistence 3.2, Validation 3.1, WS RS 4.0)
Tomcat11.0.x
Jetty12.1.x
Jackson3.0 (group ID: tools.jackson, annotations remain com.fasterxml.jackson.core)
Kotlin2.2+
Gradle8.14+ or 9.x
Maven3.6.3+

Undertow is NOT supported in Boot 4 (no Servlet 6.1 compat).

Quick-Start Workflow

Follow these steps to get a new Spring Boot 4 project running:

  1. Create project — Add the parent POM (Maven) or plugin (Gradle) shown below.
  2. Add dependencies — Include spring-boot-starter-webmvc and any other starters needed.
  3. Verify dependencies resolve — Run mvn dependency:tree (or gradle dependencies). If Jackson conflicts appear (mixing com.fasterxml.jackson and tools.jackson artifacts), see the Jackson 3.0 section before proceeding.
  4. Write main class — Annotate with @SpringBootApplication and call SpringApplication.run.
  5. Set minimal properties — At minimum, set spring.application.name and server.port.
  6. Verify startup — Run the app and confirm the embedded Tomcat starts on the configured port. Check /actuator/health if the actuator starter is present.
  7. Add first endpoint — Create a @RestController, hit it with curl or a test, confirm a 200 response. If MockMvc tests fail, ensure @AutoConfigureMockMvc is present — @SpringBootTest no longer auto-configures it in Boot 4.

Common Startup Failures

  • ClassNotFoundException on startup: Verify Jakarta EE 11 compatibility — Undertow and Jersey are not supported; switch to Tomcat 11.0.x or Jetty 12.1.x.
  • Dependency resolution errors: Confirm you are not mixing com.fasterxml.jackson and tools.jackson artifacts; see Jackson 3.0 section.
  • MockMvc tests fail to configure: Add @AutoConfigureMockMvc@SpringBootTest no longer auto-configures it in Boot 4.
  • @MockBean compilation error: Replace with @MockitoBean / @MockitoSpyBean.
  • Tracing not exporting: Property renamed from management.tracing.enabled to management.tracing.export.enabled.

Project Setup

Maven

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

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webmvc</artifactId>
    </dependency>
</dependencies>

The web starter was renamed: spring-boot-starter-web still works but the canonical name is now spring-boot-starter-webmvc.

Gradle (Kotlin DSL)

plugins {
    java
    id("org.springframework.boot") version "4.0.5"
    id("io.spring.dependency-management") version "1.1.7"
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-webmvc")
}

Minimal application.properties

spring.application.name=my-service
server.port=8080

REST Controllers (Boot 4-Specific Notes)

REST controller structure is unchanged from Boot 3. Boot 4-specific considerations:

  • Global exception handling now returns RFC 9457 Problem Details by default when spring.mvc.problemdetails.enabled=true:
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NotFoundException.class)
    public ProblemDetail handleNotFound(NotFoundException ex) {
        ProblemDetail pd = ProblemDetail.forStatusAndDetail(
            HttpStatus.NOT_FOUND, ex.getMessage());
        pd.setTitle("Resource Not Found");
        return pd;
    }
}

Enable RFC 9457 Problem Details globally: spring.mvc.problemdetails.enabled=true

  • HTTP message converters: HttpMessageConverters is deprecated — use ServerHttpMessageConvertersCustomizer and ClientHttpMessageConvertersCustomizer instead.

Configuration

application.properties

spring.application.name=my-service
server.port=8080
server.servlet.context-path=/api
spring.jackson.json.write.indent-output=true
spring.jackson.json.read.use-big-decimal-for-floats=true
logging.level.com.example=DEBUG

Note: Jackson property prefix changed — BOTH read and write paths moved:

  • spring.jackson.write.*spring.jackson.json.write.*
  • spring.jackson.read.*spring.jackson.json.read.*

Type-Safe Config with ConfigurationProperties

Prefer records with @DefaultValue (immutable, no getters/setters needed):

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;

@ConfigurationProperties("app.agent")
public record AgentProperties(
        String name,
        @DefaultValue("gpt-4") String model,
        @DefaultValue("4096") int maxTokens,
        @DefaultValue("30") int timeoutSeconds) {}

Enable with @ConfigurationPropertiesScan on the main class or @EnableConfigurationProperties(AgentProperties.class).

Important: Use @DefaultValue on record components for defaults — NOT Java field initializers (records don't have mutable fields). The @DefaultValue annotation is from org.springframework.boot.context.properties.bind.

JavaBean classes with getters/setters also work but records are preferred in Boot 4.

Profiles

Activate: spring.profiles.active=dev or --spring.profiles.active=prod

Profile-specific files: application-dev.properties, application-prod.yaml

Actuator

Dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Configuration

management.endpoints.web.exposure.include=health,info,metrics,prometheus
management.endpoint.health.show-details=always
management.endpoint.health.probes.enabled=true
management.server.port=9090

Key endpoints: /actuator/health, /actuator/metrics, /actuator/info, /actuator/prometheus

Boot 4 change: Liveness/readiness probes are enabled by default (was opt-in in 3.x).

Boot 4 change: management.tracing.enabled renamed to management.tracing.export.enabled.

Custom Health Indicator

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component
public class AgentHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        return Health.up()
            .withDetail("agent", "operational").build();
    }
}

Custom Endpoint

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
@Endpoint(id = "agent")
public class AgentEndpoint {
    @ReadOperation
    public Map<String, Object> info() {
        return Map.of("status", "running", "version", "1.0");
    }
}

Micrometer / Prometheus

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

New in Boot 4 — OpenTelemetry starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-opentelemetry</artifactId>
</dependency>

Testing in Boot 4

Critical changes from Boot 3: @MockBean is removed, MockMvc is no longer auto-configured.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

@SpringBootTest
@AutoConfigureMockMvc  // REQUIRED in Boot 4 — not auto-configured by @SpringBootTest
class ItemControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockitoBean  // replaces @MockBean (removed in Boot 4)
    private ItemService itemService;

    @Test
    void shouldReturnItems() throws Exception {
        when(itemService.findAll()).thenReturn(List.of(new Item(1L, "Test")));
        mockMvc.perform(get("/api/items"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$[0].name").value("Test"));
    }
}
Boot 3Boot 4
@MockBean@MockitoBean
@SpyBean@MockitoSpyBean
@SpringBootTest auto-configures MockMvcMust add @AutoConfigureMockMvc
org.springframework.lang.Nullableorg.jspecify.annotations.Nullable

Null Safety with JSpecify

import org.jspecify.annotations.Nullable;

public class ItemService {
    public @Nullable Item findById(Long id) {
        return repository.findById(id).orElse(null);
    }
}

org.springframework.lang.Nullable is replaced by org.jspecify.annotations.Nullable in Boot 4. Update all imports.


Key Breaking Changes from 3.x

Starter Renames

3.x4.0
spring-boot-starter-webspring-boot-starter-webmvc
spring-boot-starter-web-servicesspring-boot-starter-webservices
spring-boot-starter-aopspring-boot-starter-aspectj
spring-boot-starter-oauth2-clientspring-boot-starter-security-oauth2-client

Jackson 3.0

  • Group ID: com.fasterxml.jackson changed to tools.jackson (except annotations — com.fasterxml.jackson.core annotations remain)
  • @JsonComponent changed to @JacksonComponent
  • @JsonMixin changed to @JacksonMixin
  • JsonObjectSerializer changed to ObjectValueSerializer
  • Jackson2ObjectMapperBuilderCustomizer changed to JsonMapperBuilderCustomizer
  • Properties: spring.jackson.read.* / spring.jackson.write.* moved to spring.jackson.json.read.* / spring.jackson.json.write.*
  • Default: properties sort alphabetically, dates serialize as ISO-8601
  • Jackson 2 compat module: spring-boot-jackson2 (deprecated)

Jakarta EE 11

  • Servlet 6.1 required (Undertow dropped, Jersey dropped)
  • Tomcat 11.0.x and Jetty 12.1.x only

Null Safety

  • org.springframework.lang.Nullable replaced by org.jspecify.annotations.Nullable

Testing

  • @MockBean / @SpyBean removed — use @MockitoBean / @MockitoSpyBean
  • @SpringBootTest no longer auto-configures MockMvc — add @AutoConfigureMockMvc

HTTP Message Converters

  • HttpMessageConverters deprecated — use ServerHttpMessageConvertersCustomizer and ClientHttpMessageConvertersCustomizer

Other Property Changes

  • management.tracing.enabled changed to management.tracing.export.enabled
  • Liveness/readiness probes enabled by default (was opt-in)

skills

spring-boot-4

tile.json