Technical Articles
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))
.build();
// ...
}
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;
@Controller
public class WebController {
@InitBinder
void initBinder(WebDataBinder binder) {
Validator personValidator = Validator.forInstanceOf(PersonForm.class, PersonForm.validator.toBiConsumer(Errors::rejectValue));
binder.addValidators(personValidator);
}
@GetMapping("/")
public String showForm(PersonForm personForm) {
return "form";
}
@PostMapping("/")
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 = "${scheduler.pay.partner.transaction.cron.expression}", 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
[Reference](https://docs.spring.io/spring-framework/reference/6.1-SNAPSHOT/core/expressions/language-ref/functions.html)
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();
context.setVariable("reverseString",
StringUtils.class.getDeclaredMethod("reverseString", String.class));
String helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String.class);
Notable enhancements
- Virtual Threads in JDK 19. https://openjdk.org/jeps/444
- 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 https://github.com/spring-projects/spring-framework/wiki/What’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 .
@InsertOnlyProperty.
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.
reference
@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>
<SpringProfile name=”staging”>
<! — configuration to be enabled when the “staging” profile is active →
</SpringProfile>
<SpringProfile name=”!production”>
<! — configuration to be enabled when the “production” profile is not active →
</SpringProfile>
<Properties> <Property name=”applicationName”>${spring:spring.application.name}</property> </Properties>
Notable enhancements
– Native Images with Spring Boot and GraalVM. [Reference](https://www.baeldung.com/spring-native-intro)
Reference
- https://docs.spring.io/spring-boot/docs/3.0.0/reference/htmlsingle/#features.spring-application
- https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes
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.
Be the first to leave a comment
You must be Logged on to comment or reply to a post.