skills/backend/spring-boot/3.x/SKILL.md
Version-specific expert for Spring Boot 3.x (3.0 through 3.5). Covers Jakarta EE migration, GraalVM native image, Micrometer Observation API, HTTP interface clients, ProblemDetail, virtual threads, RestClient, JdbcClient, CDS, structured logging, and the SecurityFilterChain model. WHEN: "Spring Boot 3", "Spring Boot 3.0", "Spring Boot 3.1", "Spring Boot 3.2", "Spring Boot 3.3", "Spring Boot 3.4", "Spring Boot 3.5", "Jakarta migration", "javax to jakarta", "Spring Boot native image", "RestClient", "JdbcClient", "virtual threads Spring", "@HttpExchange", "@ServiceConnection".
npx skillsauth add chrishuffman5/domain-expert backend-spring-boot-3xInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are a specialist in Spring Boot 3.x (3.0 through 3.5), covering the current LTS-track release line. As of April 2026, only 3.5 remains in OSS support (ends June 2026). Spring Boot 4.0 is the current major version.
For foundational Spring Boot knowledge (IoC, auto-configuration, MVC, Security, Data, Actuator), refer to the parent technology agent. This agent focuses on what is new or changed in the 3.x line.
| Version | Released | OSS Support Ends | Status (Apr 2026) | |---|---|---|---| | 3.0 | Nov 2022 | Dec 2023 | EOL | | 3.1 | May 2023 | Jun 2024 | EOL | | 3.2 | Nov 2023 | Dec 2024 | EOL | | 3.3 | May 2024 | Jun 2025 | Commercial only | | 3.4 | Nov 2024 | Dec 2025 | Commercial only | | 3.5 | May 2025 | Jun 2026 | OSS supported |
Recommendation: If starting a new project, use Spring Boot 4.0. Upgrade existing 3.x projects to 3.5 first, then to 4.0.
This is the most impactful change in the 3.x line. Every javax.* package from Java EE moved to jakarta.*. There is no compatibility bridge.
| Old (javax) | New (jakarta) |
|---|---|
| javax.servlet | jakarta.servlet |
| javax.persistence | jakarta.persistence |
| javax.validation | jakarta.validation |
| javax.annotation | jakarta.annotation |
| javax.transaction | jakarta.transaction |
| javax.websocket | jakarta.websocket |
| javax.mail | jakarta.mail |
| javax.inject | jakarta.inject |
| javax.jms | jakarta.jms |
// Spring Boot 2.x
import javax.servlet.http.HttpServletRequest;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
// Spring Boot 3.x
import jakarta.servlet.http.HttpServletRequest;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;
<plugin>
<groupId>org.openrewrite.maven</groupId>
<artifactId>rewrite-maven-plugin</artifactId>
<version>6.36.0</version>
<configuration>
<activeRecipes>
<recipe>org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_0</recipe>
</activeRecipes>
</configuration>
<dependencies>
<dependency>
<groupId>org.openrewrite.recipe</groupId>
<artifactId>rewrite-spring</artifactId>
<version>5.x</version>
</dependency>
</dependencies>
</plugin>
./mvnw rewrite:dryRun # preview
./mvnw rewrite:run # apply
The composite recipe handles: Jakarta namespace, Java 17 upgrade, Spring Framework 6, Spring Security 6, Hibernate 6.1, Spring Batch 4 to 5, property renames, and more.
Java 17 is the minimum for all 3.x releases. Java 21 unlocks virtual threads (3.2+).
// Record DTO -- works with @RequestBody, @ResponseBody, Spring Data projections
public record CreateUserRequest(
@NotBlank String username,
@Email String email
) {}
@PostMapping("/users")
public ResponseEntity<UserDto> create(@RequestBody @Valid CreateUserRequest request) { ... }
// Record-based @ConfigurationProperties
@ConfigurationProperties("app.mail")
public record MailProperties(String host, int port, boolean ssl) {}
public sealed interface PaymentResult
permits PaymentResult.Success, PaymentResult.Failed, PaymentResult.Pending {
record Success(String transactionId, BigDecimal amount) implements PaymentResult {}
record Failed(String reason, int errorCode) implements PaymentResult {}
record Pending(String referenceId) implements PaymentResult {}
}
String describe(PaymentResult result) {
return switch (result) {
case PaymentResult.Success s -> "Paid: " + s.transactionId();
case PaymentResult.Failed f -> "Failed: " + f.reason();
case PaymentResult.Pending p -> "Pending: " + p.referenceId();
};
}
Spring Boot 3.0 provides first-class GraalVM native image support via AOT processing. The AOT engine runs at build time, generating static bean definitions and GraalVM hint files.
# Docker (no local GraalVM needed)
./mvnw -Pnative spring-boot:build-image
# Local native compile
./mvnw -Pnative native:compile
public class MyRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints.reflection().registerMethod(
ReflectionUtils.findMethod(MyClass.class, "process", String.class),
ExecutableMode.INVOKE);
hints.resources().registerPattern("templates/*.html");
}
}
@SpringBootApplication
@ImportRuntimeHints(MyRuntimeHints.class)
public class MyApplication { ... }
For serialized classes: @RegisterReflectionForBinding(UserDto.class).
Key limitations: Closed-world assumption, no @Profile switching, @MockBean/@SpyBean unsupported, reflection requires explicit hints. See parent agent references/best-practices.md for the full limitations table.
Replaces Spring Cloud Sleuth with a unified API tying metrics, tracing, and logging together.
management:
tracing:
sampling:
probability: 1.0 # default is 0.1
opentelemetry:
tracing:
export:
otlp:
endpoint: http://localhost:4318/v1/traces
Spring Boot automatically injects traceId and spanId into MDC:
logging:
pattern:
correlation: "[${spring.application.name:},%X{traceId:-},%X{spanId:-}] "
@Service
public class UserService {
private final ObservationRegistry observationRegistry;
public User createUser(String email) {
return Observation.createNotStarted("user.create", observationRegistry)
.lowCardinalityKeyValue("operation", "create")
.highCardinalityKeyValue("email", email)
.observe(() -> doCreateUser(email));
}
}
@Service
public class PaymentService {
@Observed(name = "payment.process", contextualName = "processing-payment")
public PaymentResult processPayment(PaymentRequest request) {
return doProcess(request);
}
}
Declarative HTTP interfaces replacing verbose WebClient/RestTemplate boilerplate:
@HttpExchange(url = "/users", contentType = MediaType.APPLICATION_JSON_VALUE)
public interface UserClient {
@GetExchange("/{id}")
User getById(@PathVariable Long id);
@PostExchange
ResponseEntity<Void> create(@RequestBody CreateUserRequest request);
@DeleteExchange("/{id}")
void delete(@PathVariable Long id);
}
@Bean
UserClient userClient(RestClient.Builder builder) {
RestClient restClient = builder.baseUrl("https://api.example.com").build();
return HttpServiceProxyFactory
.builderFor(RestClientAdapter.create(restClient))
.build()
.createClient(UserClient.class);
}
Available annotations: @GetExchange, @PostExchange, @PutExchange, @PatchExchange, @DeleteExchange.
spring:
mvc:
problemdetails:
enabled: true
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(RecordNotFoundException.class)
public ProblemDetail handleNotFound(RecordNotFoundException ex) {
ProblemDetail body = ProblemDetail.forStatusAndDetail(
HttpStatus.NOT_FOUND, ex.getMessage());
body.setTitle("Record Not Found");
body.setProperty("errorCode", "REC_404");
return body;
}
}
All Spring MVC exceptions implement ErrorResponse and serialize as application/problem+json automatically when enabled.
WebSecurityConfigurerAdapter removed. Lambda DSL is the only approach:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
}
Key API changes from 2.x:
antMatchers() -> requestMatchers()authorizeRequests() -> authorizeHttpRequests().and() removed -- use lambda DSLspring-boot-starter-oauth2-authorization-server added in 3.1spring:
threads:
virtual:
enabled: true
When enabled: Tomcat, Jetty, @Async, and scheduled tasks all use virtual threads. Standard blocking code scales without thread pool sizing concerns:
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// Blocking JDBC -- scales fine on virtual threads
return userRepository.findById(id).orElseThrow();
}
}
Impact: Eliminates the main motivation for adopting WebFlux in I/O-bound applications.
Modern, fluent, synchronous HTTP client replacing RestTemplate:
@Service
public class PostService {
private final RestClient restClient;
public PostService(RestClient.Builder builder) {
this.restClient = builder
.baseUrl("https://jsonplaceholder.typicode.com")
.build();
}
public List<Post> findAll() {
return restClient.get()
.uri("/posts")
.retrieve()
.body(new ParameterizedTypeReference<>() {});
}
public Post findById(Long id) {
return restClient.get()
.uri("/posts/{id}", id)
.retrieve()
.body(Post.class);
}
}
Fluent JDBC API without Spring Data JPA:
@Repository
public class UserRepository {
private final JdbcClient jdbcClient;
public UserRepository(JdbcClient jdbcClient) {
this.jdbcClient = jdbcClient;
}
public Optional<User> findById(Long id) {
return jdbcClient.sql("SELECT * FROM users WHERE id = :id")
.param("id", id)
.query(User.class)
.optional();
}
public int save(User user) {
return jdbcClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)")
.param("name", user.name())
.param("email", user.email())
.update();
}
}
~40-50% startup reduction without native image limitations:
# Training run
java -XX:ArchiveClassesAtExit=./application.jsa \
-Dspring.context.exit=onRefresh -jar target/myapp.jar
# Production run
java -XX:SharedArchiveFile=application.jsa -jar target/myapp.jar
With Buildpacks: BP_JVM_AOTCACHE_ENABLED=true ./mvnw spring-boot:build-image
@SpringBootTest
@Testcontainers
class IntegrationTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
@Container
@ServiceConnection
static RedisContainer redis = new RedisContainer("redis:7");
// No manual @DynamicPropertySource needed
}
Supports: PostgreSQL, MySQL, MariaDB, MongoDB, Redis, Kafka, RabbitMQ, Cassandra, Elasticsearch, Neo4j, ActiveMQ, Artemis, LDAP, Couchbase, R2DBC variants.
logging:
structured:
format:
console: ecs # Elastic Common Schema
# console: logstash # Logstash JSON
# console: gelf # Graylog Extended Log Format
Output:
{
"@timestamp": "2024-01-15T10:23:45.123Z",
"log.level": "INFO",
"message": "User created successfully",
"service.name": "my-service",
"trace.id": "803b448a0489f84084905d3093480352"
}
Captures a running JVM checkpoint and restores it for near-instant startup while maintaining JIT-warmed throughput. Linux only. Spring Boot provides SmartLifecycle integration to properly close and reopen resources around checkpoints.
| Feature | Version |
|---|---|
| Jakarta EE 9 (javax -> jakarta) | 3.0 |
| Java 17 minimum | 3.0 |
| GraalVM native image (first-class) | 3.0 |
| Micrometer Observation API | 3.0 |
| @HttpExchange declarative clients | 3.0 |
| ProblemDetail / RFC 7807 | 3.0 |
| SecurityFilterChain (WebSecurityConfigurerAdapter removed) | 3.0 |
| spring-authorization-server starter | 3.1 |
| @ServiceConnection (Testcontainers) | 3.1 |
| Virtual threads (spring.threads.virtual.enabled) | 3.2 (Java 21) |
| RestClient | 3.2 |
| JdbcClient | 3.2 |
| CRaC support | 3.2 |
| Class Data Sharing (CDS) | 3.3 |
| @ServiceConnection expanded | 3.3 |
| Structured logging (ECS, Logstash, GELF) | 3.4 |
Spring Boot 3.5 is the bridge release. Upgrade to 3.5 first -- it deprecates everything removed in 4.0, giving compiler warnings without breaking your build. Then move to 4.0. See 4.0/SKILL.md for full migration guidance.
tools
kubectl command-line usage and scripting: kubeconfig and context management, output formats (jsonpath, custom-columns, go-template), all major verbs (get, describe, apply, delete, exec, logs, port-forward, rollout, scale, drain), workload resources, config/storage, networking, RBAC, node management, debugging (CrashLoopBackOff, ImagePullBackOff, OOMKilled), and scripting patterns (dry-run, diff, wait, jq, kustomize). WHEN: "kubectl", "k8s CLI", "kubeconfig", "namespace", "pod", "deployment", "service", "ingress", "configmap", "secret", "rollout", "scale", "drain", "taint", "kustomize". Do NOT use for cluster architecture, sizing, upgrades, or workload design decisions — that's the `kubernetes` skill in the `containers` plugin. This skill is command syntax and scripting kubectl against an existing cluster, not cluster ops.
tools
Bash 5.x shell scripting, Unix text processing, and command-line automation: variables, parameter expansion, quoting, control flow, functions, I/O redirection, error handling (set -euo pipefail, trap), and the Unix tool ecosystem (grep, sed, awk, jq, find, sort, uniq, cut, xargs). Covers process management, networking (curl, ssh, rsync, nc), file locking (flock), parallel execution, and production script patterns. WHEN: "Bash", "bash", "shell", "sh", ".sh", "shell script", "sed", "awk", "grep", "jq", "find", "xargs", "curl", "ssh", "rsync", "cron", "pipe", "redirect", "here-doc", "shebang", "POSIX", "set -euo pipefail", "trap".
tools
Azure CLI (az) command syntax and scripting: authentication (interactive, service principal, managed identity, SSO), output formats and JMESPath queries, resource groups, VMs, storage accounts/blobs, networking (VNets, NSGs, load balancers, DNS), Entra ID, AKS, App Service, Functions, databases (SQL, Cosmos DB, MySQL, PostgreSQL), Key Vault, Monitor/alerting, and infrastructure scripting patterns. WHEN: "az ", "Azure CLI", "az login", "az vm", "az aks", "az storage", "az keyvault", "az monitor", "az ad", "az group", "az network", "az webapp", "az functionapp", "az sql", "az cosmosdb", "JMESPath", "az account". Do NOT use for Azure architecture, landing zones, or multi-subscription strategy — that's the `cloud-platforms` plugin. This skill is about command syntax and scripting the CLI, not deciding what to provision.
tools
AWS CLI v2 command syntax and scripting: authentication (profiles, SSO, assume-role, instance profiles), output formats and JMESPath queries, pagination and waiters, IAM, S3, Lambda, RDS, CloudFormation, ECS, EKS, CloudWatch, SSM, Route 53, STS, and VPC networking. WHEN: "aws ", "AWS CLI", "aws ec2", "aws s3", "aws lambda", "aws iam", "aws cloudformation", "aws ssm", "aws ecs", "aws eks", "aws rds", "aws cloudwatch", "aws route53", "aws sts". Do NOT use for AWS architecture, service selection, multi-account strategy, or FinOps — that's the `cloud-platforms` plugin. This skill is about command syntax and scripting the CLI, not deciding what to provision.