Skip to Content
Technical Articles
Author's profile photo Dhiraj Jaiswal

Spring Boot 3 feature updates & best practices

Introduction: Spring Boot 3.0 will require Java 17 and spring framework 6 hence it becomes important for developers to get familiar with the changes and enhancements done in spring libraries as well as third-party library to adopt the new technology as part of best practices. In this blog I have tried to cover recent enhancements that are done in spring project using which code base can be simplified and developer experience can be improved.

Spring Framework 6 changes

Validator factory: A functional approach to using validator.

Introduction of method in Validator to provide a way to create a validator for the specific type <T> and define the validator in a functional way. reference
package com.example.validatingforminput;

import am.ik.yavi.builder.ValidatorBuilder;
import am.ik.yavi.core.Validator;

public class PersonForm {

    public static Validator<PersonForm> validator = ValidatorBuilder.<PersonForm>of()
            .constraint(PersonForm::getName, "name", c -> c.notNull().greaterThanOrEqual(2).lessThanOrEqual(30))
            .constraint(PersonForm::getAge, "age", c -> c.notNull().greaterThanOrEqual(18))

    // ...
package com.example.validatingforminput;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;

public class WebController {

    void initBinder(WebDataBinder binder) {
        Validator personValidator = Validator.forInstanceOf(PersonForm.class, PersonForm.validator.toBiConsumer(Errors::rejectValue));

    public String showForm(PersonForm personForm) {
        return "form";

    public String checkPersonInfo(@Validated PersonForm personForm, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            return "form";

        return "redirect:/results";

New methods in DataAccessUtils

DataAccessUtils provides an optionalResult method for Collection as well as Stream arguments.

protected final <T> T findSingle(String query) {
    return (T) DataAccessUtils.optionalResult(em.createNamedQuery(query).getResultList());

Multiple TaskSchedulers to be supported with @Scheduled

Now we can have multiple scheduled tasks in our application, with multiple their pool size, thread name and some as daemon. Previously a single application could have only 1 task scheduler. Now we could define multiple schedulers and refer them in the @Scheduled annotation. reference
    @Scheduled(scheduler="${name}",cron = "${}", zone = "UTC")

Spring Language Expression(SpEL) enchancement

We can extend SpEL by registering user-defined functions that can be called within the expression string.The function is registered through the EvaluationContext. The following example shows how to register a user-defined function to be invoked via reflection (i.e. a Method).Reference

public abstract class StringUtils {

	public static String reverseString(String input) {
		StringBuilder backwards = new StringBuilder(input.length());
		for (int i = 0; i < input.length(); i++) {
			backwards.append(input.charAt(input.length() - 1 - i));
		return backwards.toString();
ExpressionParser parser = new SpelExpressionParser();

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
		StringUtils.class.getDeclaredMethod("reverseString", String.class));

String helloWorldReversed = parser.parseExpression(
		"#reverseString('hello')").getValue(context, String.class);

Notable enhancements

  • Virtual Threads in JDK 19.
  • New PartEvent API to stream multipart form uploads
  • In ThreadPoolTaskExecuter we can pause/resume.
  • For reactive java, support of cache/scheduled etc.
  • The new RestClient is a synchronous HTTP client that offers an API similar to WebClient, using the same infrastructure as RestTemplate.
  • Support for recording asynchronous events with @RecordApplicationEvents in testing
  • JdbcClient for JdbcTemplate with fluent api’s-New-in-Spring-Framework-6.x
Spring Data changes

New CRUD repository interfaces that return List instead of Iterable

Spring Data offers new variants of the CRUD repositories. These return a List for methods that return multiple entities:
– ListCrudRepository
– ListQuerydslPredicateExecutor
– ListQueryByExampleExecutor

They can be used as a drop-in replacement for the interfaces of same name but without the List prefix.

SpEL support in Spring Data JPA @Query definitions

Spring Data JDBC now does support SpEL expressions in @Query annotations and named queries. It works just as for Spring Data JPA .


You may now annotate properties of the aggregate root with @InsertOnlyProperty. Properties annotated in such way will be written to the database only during insert operations, but they will not be updated afterwards.

@SpringBootTest with Main Methods

The @SpringBootTest annotation can now use the main of any discovered @SpringBootConfiguration class if it’s available. This means that any custom SpringApplication configuration performed by your main method can now be picked up by tests.
To use the main method for a test set the useMainMethod attribute of @SpringBootTest to UseMainMethod.ALWAYS or UseMainMethod.WHEN_AVAILABLE.


Observability with Spring Boot 3

Spring boot 3 contain numerous autoconfigurations for improved metrics with Micrometer and new distributed tracing support with Micrometer Tracing. A very detailed explanation is provided here

Log4j2 Enhacements

Spring Boot 3 has introduced new Log4j2 extensions that includes the following additional features:
– Profile-specific Configuration
– Environment Properties Lookup
– Log4j2 System Properties

<SpringProfile name=”dev | test”>
<! — configuration to be enabled when the “dev” or “test” profiles are active →

<SpringProfile name=”staging”>
<! — configuration to be enabled when the “staging” profile is active →

<SpringProfile name=”!production”>
<! — configuration to be enabled when the “production” profile is not active →

<Properties> <Property name=”applicationName”>${}</property> </Properties>
Notable enhancements
– Native Images with Spring Boot and GraalVM. [Reference](



If you want to know about JDK 17 feature please have a loot at my previous blog Java 17 – Best Practices and features.
Please share your thoughts about this blog in the comment section.

Please follow my Dhiraj Jaiswal for future posts.

Assigned Tags

      Be the first to leave a comment
      You must be Logged on to comment or reply to a post.