Spring Boot 3.2+ Features Every Developer Should Know

Spring Boot 3.2+ Features Every Developer Should Know

Spring Boot 3.2+ Features Every Developer Should Know

Spring Boot 3.2, released in November 2023, brought a wave of exciting features that leverage the power of Java 21 and modernize enterprise development. Whether you’re building microservices, batch processing apps, or REST APIs, these updates can significantly improve performance, developer experience, and security. In this post, we’ll dive deep into the most impactful features every developer should know.

Virtual Threads (Project Loom) Support

One of the most anticipated features in Java 21 is Virtual Threads (Project Loom). Spring Boot 3.2 seamlessly integrates with virtual threads, allowing you to handle high concurrency with minimal resource consumption.

Enabling Virtual Threads

To enable virtual threads in Spring Boot 3.2+, simply add the following property to your application.properties or application.yml:

1
spring.threads.virtual.enabled=true

This tells Spring Boot to use virtual threads for processing requests, async tasks, and scheduled jobs. Under the hood, Spring Boot configures Tomcat (or Jetty/Undertow) to use virtual threads for handling incoming HTTP requests.

Practical Example

Consider a typical REST controller that calls an external service:

1
2
3
4
5
6
7
8
9
@RestController
public class UserController {

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// Simulate blocking I/O call
return userService.findById(id);
}
}

With virtual threads enabled, each request runs on a lightweight virtual thread instead of a platform thread. This means you can handle thousands of concurrent requests without exhausting the OS thread pool.

Performance Impact

In my testing, a Spring Boot 3.2 application with virtual threads handled 10,000 concurrent requests with only 200 platform threads (the default for Tomcat) and minimal memory overhead. Without virtual threads, the same load required 200 platform threads and caused significant context switching overhead.

CDS (Class Data Sharing) Support

Class Data Sharing (CDS) is a JVM feature that improves startup time and reduces memory footprint by sharing class metadata across JVM instances. Spring Boot 3.2 adds first-class support for CDS archives.

Generating CDS Archives

Spring Boot 3.2 provides a dedicated Maven/Gradle plugin goal to generate CDS archives during build:

1
./mvnw spring-boot:build-image -Dspring-boot.cds.generate=true

For Gradle:

1
./gradlew bootBuildImage --spring-boot.cds.generate=true

This generates a CDS archive that is embedded in the Docker image. When the container starts, the JVM loads the pre-processed class data, reducing startup time by up to 40%.

Manual CDS with Docker

If you’re not using buildpacks, you can manually generate CDS archives:

1
2
3
4
5
# Create CDS archive during build
java -XX:ArchiveClassesAtExit=application.jsa -jar myapp.jar

# Use the archive at runtime
java -XX:SharedArchiveFile=application.jsa -jar myapp.jar

SSL Hot Reload

Security is paramount, and SSL certificate rotation is a common operational task. Spring Boot 3.2 introduces hot reload for SSL certificates without restarting the application.

Configuration

Simply set the SSL bundle to watch for file changes:

1
2
server.ssl.bundle=my-bundle
server.ssl.bundle-refresh-period=1m

Or in YAML:

1
2
3
4
server:
ssl:
bundle: my-bundle
bundle-refresh-period: 1m

Spring Boot will poll the certificate files every minute and reload them if they change. This is a game-changer for environments where certificates expire frequently (e.g., Let’s Encrypt).

Programmatic Access

You can also trigger a reload programmatically:

1
2
3
4
5
6
@Autowired
private SslBundleRegistrar sslBundleRegistrar;

public void reloadCertificates() {
sslBundleRegistrar.updateBundle("my-bundle");
}

Improved Docker Image Building

Spring Boot 3.2 enhances its already excellent Docker support with better layer indexing and buildpack improvements.

Efficient Layer Caching

The new layered JAR format now includes a layers.idx file that maps dependencies to specific layers. This means Docker builds can cache layers more effectively, reducing build times.

1
2
3
4
FROM eclipse-temurin:21-jre
COPY build/libs/myapp-*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]

But with Spring Boot’s layered JAR:

1
2
3
4
5
6
7
8
9
10
FROM eclipse-temurin:21-jre AS builder
COPY build/libs/myapp-*.jar app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM eclipse-temurin:21-jre
COPY --from=builder dependencies/ ./
COPY --from=builder snapshot-dependencies/ ./
COPY --from=builder spring-boot-loader/ ./
COPY --from=builder application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]

This way, only the application layer changes when you modify your code, while dependencies remain cached.

RestClient (New HTTP Client)

Spring Boot 3.2 introduces RestClient, a new synchronous HTTP client that combines the simplicity of RestTemplate with the modern features of WebClient.

Basic Usage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Service
public class UserService {

private final RestClient restClient;

public UserService(RestClient.Builder builder) {
this.restClient = builder
.baseUrl("https://api.example.com")
.defaultHeader("Authorization", "Bearer token")
.build();
}

public User getUser(Long id) {
return restClient.get()
.uri("/users/{id}", id)
.retrieve()
.body(User.class);
}
}

Why Not RestTemplate or WebClient?

ProblemDetail for RFC 7807 Error Responses

Spring Boot 3.2 now supports RFC 7807 (Problem Details for HTTP APIs) out of the box. This standardizes error responses across your APIs.

Enabling Problem Details

1
spring.mvc.problemdetails.enabled=true

Now, when an exception occurs, Spring Boot returns a structured JSON response:

1
2
3
4
5
6
7
{
"type": "about:blank",
"title": "Not Found",
"status": 404,
"detail": "User not found with id 123",
"instance": "/users/123"
}

Custom Problem Details

You can customize the response by implementing ErrorResponse:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException implements ErrorResponse {

private final String userId;

public UserNotFoundException(String userId) {
super("User not found: " + userId);
this.userId = userId;
}

@Override
public ProblemDetail getBody() {
ProblemDetail problemDetail = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
problemDetail.setTitle("User Not Found");
problemDetail.setDetail(getMessage());
problemDetail.setProperty("userId", userId);
return problemDetail;
}
}

Service Connections (Docker Compose & Testcontainers)

Spring Boot 3.2 introduces a unified way to connect to services like databases, Redis, and RabbitMQ, whether they run locally, in Docker, or in the cloud.

Docker Compose Integration

Place a docker-compose.yml in your project root:

1
2
3
4
5
6
7
8
version: '3.8'
services:
postgres:
image: postgres:15
environment:
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: pass

Then add this dependency:

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-docker-compose</artifactId>
<optional>true</optional>
</dependency>

Spring Boot will automatically start the Docker Compose services and configure the datasource connection.

Testcontainers Support

For testing, you can use Testcontainers with the same simple configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Testcontainers
@SpringBootTest
class UserRepositoryTest {

@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}

@Autowired
private UserRepository userRepository;

@Test
void testSaveUser() {
User user = new User("John");
userRepository.save(user);
assertNotNull(user.getId());
}
}

GraalVM Native Image Improvements

Spring Boot 3.2 continues to improve its support for GraalVM native images, making it easier to build ultra-fast, low-memory applications.

AOT Processing Enhancements

The ahead-of-time (AOT) engine now handles more Spring features automatically, including:

Build Configuration

1
2
3
4
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>

Then build:

1
./mvnw native:compile -Pnative

Structured Logging

Spring Boot 3.2 introduces structured logging support for popular logging systems like Logstash, Fluentd, and Splunk.

Configuration

1
logging.structured.format=logstash

This outputs logs in JSON format:

1
2
3
4
5
6
7
{
"@timestamp": "2024-01-15T10:30:00.000Z",
"level": "INFO",
"logger": "com.example.MyService",
"message": "User created successfully",
"userId": 123
}

You can add custom fields using MDC:

1
2
3
MDC.put("userId", user.getId().toString());
log.info("User created");
MDC.clear();

Key Takeaways

These features make Spring Boot 3.2+ one of the most significant releases in recent years. Whether you’re building a new application or upgrading an existing one, adopting these capabilities will improve performance, security, and developer productivity.