spring framework
- DDD with spring
- distributed scheduling with spring boot
- hexagonal architecture with spring boot
- how to add MDC in Spring MVC application
- how to set locale for java bean validations in Spring
- how to test spring-boot autoconfigurations
- making your spring beans intelligent
- migrating from spring data jpa to spring data jdbc
- reduce spring-boot app memory consumption
- spring boot 3.4 unwrapped
- spring boot testing zero to hero
- spring data cookbook
- spring framework - filter vs dispatcher servlet vs interceptor vs controller
- spring restclient debug log
- spring security 6 baked with spring boot 3 recipe
- spring security architecture principles
- spring security the good parts
- spring-boot virtual threads vs webflux
- spring-modulith
- transactional outbox pattern with spring boot
spring 6 / spring-boot 3.0
GA: 2022-11
Observability
Monitoring vs observability:
- monitoring is about setting graphs beforehand to know when something goes bad
- observability is a tool you can use to really understand what’s going on in your distributed system
Instrumenting strategies:
- java agent, at the bytecode level
- using public extension points
- direct instrumentation in the library
Micrometer 1.10.0 introduces a new io.micrometer.observation.Observation
that will be used for observability in Spring 6.
Observation.createNotStarted("spring.talk", registry)
.lowCardinalityKeyValue("conferance", "devoxx")
.observe(() -> {
log.info("Talk about observations");
String response = restTemplate.getForObject(
"https://devoxx.be",
String.class
);
});
It will propagate the correlation id to the request.
logback-spring.xml
In order to use spring logback extensions, we need to use logback-spring.xml
and not logback.xml
.
Problem details for HTTP APIs
RFC 7807
The current error messages provided by default by Spring MVC and WebFlux are too vague:
HTTP/1.1 500
Content-Type: application/json
{
"timestamp": "2022-10-07T10:10:10.888+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "No message available",
"path": "/projects/spring-devoxx"
}
With in Spring 6:
HTTP/1.1 404
Content-Type: application/problem+json
{
"type": "https://spring.io/api/problems/unknown-project",
"title": "Unknown Spring project",
"status": 404,
"detail": "No project found for id 'spring-devoxx'",
"instance": "/projects/spring-devoxx"
}
- the
Content-Type
is a new content type. - a
type
field that contains a URL that can be provided to the user for documentation - a
title
field that is fixed, like a category of error
We can customize using the annotation @ControllerAdvice
and @ExceptionHandler
, like before. However, we can return a ProblemDetail
instead:
@ControllerAdvice(annotations = RestController.class)
class ApiControllerAdvice {
@ExceptionHandler(UnknownProjectException.class)
ProblemDetail handlUnknownProjectException(UnknownProjectException e) {
ProblemDetail problemDetail = ProblemDetail.froStatus(HttpStatus.NOT_FOUND);
problemDetail.setType(URI.create("https://example.com/problems/project"));
problemDetail.setTile("Unknown Spring Project");
problemDetail.setDetail(String.format("Project '%s' not found", e.getSlug()));
return problemDetail;
}
}
To enable this feature, add in the application.properties
:
spring.mvc.problemdetails.enabled=true
AOT and GraalVM support
By default, no custom configuration for GraalVM.
Native source and resource are generated at target/generated/aot*
.
Java interface client
interface SpringProjectsClient {
@GetExchange("/spring-projects/{slug}")
ProjectSummary findProject(@PathVariable String slug);
}
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
@Bean
HttpServiceProxyFactory httpServiceProxyFactory(WebClient.Builder webClientBuilder) {
return WebClientAdapter.createHttpServiceProxyFactory(
webClientBuilder.baseUrl("http://localhost:8080")
);
}
@Bean
SpringProjectsClient springProjectClient(HttpServiceProxyFactory httpServiceProxyFactory) {
return httpServiceProxyFactory.createClient(SpringProjects.client.class);
}
@Bean
ApplicationRunner cli(SpringProjectsClient client) {
return args -> {
System.out.println(client.getProjectSummary("spring-boot"));
};
}
}
Like Retrofit, instead of using RestTemplate
.
Also support other transports, e.g. @RSocketExchange
for RSocket.
Migration
- Upgrading spring-boot to 2.7 from 2.6
- Upgrading spring-boot to 3.0 from 2.7
- Spring Security without the WebSecurityConfigurerAdapter
- Upgrading to Spring Framework 6.x
Issues encountered
Using deprecated ‘-debug’ fallback for parameter name resolution. Compile the affected code with ‘-parameters’ instead or avoid its introspection
Can be fixed.
Got a warning on application startup. The solution was to add:
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
Source: Warning printed after migrating to Spring Boot 3.0 & Spring Integration 6.0
Missing parameter metadata for RetryMode
Can be fixed.
When starting my app, I got the following warning:
Missing parameter metadata for RetryMode(String, int), which declares implicit or synthetic parameters. Automatic resolution of generic type information for method parameters may yield incorrect results if multiple parameters have the same erasure. To solve this, compile your code with the '-parameters' flag.
When debugging the application, it seems to come from private software.amazon.awssdk.core.retry.RetryMode(java.lang.String,int)
.
The application was using a custom property s3.http-client.retry-mode="ADAPTIVE"
, which is automatically deserialized by Spring into an object RetryMode
.
By switching into a String
, then using RetryMode.valueOf(String)
instead, we no longer have the warning.
No AuthenticationManager
Can be fixed.
When migrating the WebSecurityConfigurerAdapter
, we used the abstract
method authenticationManager()
. However, with the new spring-security 6, there are no such method.
We need to declare a new bean:
@Bean
AuthenticationManager authenticationManager(
AuthenticationConfiguration authenticationConfiguration
) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
springdoc-openapi
Can be fixed.
The library that generates the openapi documentation also supports spring-boot 3. However, instead of depending on springdoc-openapi-ui
, we need to depend on springdoc-openapi-starter-webmvc-ui
.
Source: spring-boot 3 support for springdoc-openapi
No logs displayed on runtime in the converter-worker
Can be fixed.
When starting the application using default spring profile, there are no logs displayed in the console.
It seems there are multiple logback-spring.xml
and the converter-worker seems to pick up on that does not display any logs for some reason.
Indeed, when using another logback.xml
, e.g. logback-local.xml
, the logs are displayed correctly.
Make be linked to the below issue where some dependency is using com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider
.
Error NoClassDefFoundError XmlElement
on startup
Can be fixed.
In the app, when starting the application using the following logback.xml
(not logback-spring.xml
, as it seems the classpath may contain multiple logback-spring.xml
)
Exception in thread "main" java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlElement
at com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector.<init>(JaxbAnnotationIntrospector.java:137)
at com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector.<init>(JaxbAnnotationIntrospector.java:124)
at com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule.setupModule(JaxbAnnotationModule.java:98)
at com.fasterxml.jackson.databind.ObjectMapper.registerModule(ObjectMapper.java:879)
at com.fasterxml.jackson.databind.ObjectMapper.registerModules(ObjectMapper.java:1081)
at com.fasterxml.jackson.databind.ObjectMapper.findAndRegisterModules(ObjectMapper.java:1165)
at net.logstash.logback.composite.AbstractCompositeJsonFormatter.createJsonFactory(AbstractCompositeJsonFormatter.java:247)
at net.logstash.logback.composite.AbstractCompositeJsonFormatter.start(AbstractCompositeJsonFormatter.java:117)
at net.logstash.logback.LogstashFormatter.start(LogstashFormatter.java:135)
at net.logstash.logback.encoder.CompositeJsonEncoder.start(CompositeJsonEncoder.java:129)
There is a dependency on com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider
in the converter-worker, which provides the javax.xml
version and not jakarta.xml
. Instead, we should use com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider
.
The transitive dependencies were: app <- lib1 <- lib2 <- jackson-jaxrs-json-provider
Solution: replace the jackson-jaxrs-json-provider
<dependency>
<groupId>com.fasterxml.jackson.jakarta.rs</groupId>
<artifactId>jackson-jakarta-rs-json-provider</artifactId>
</dependency>
<dependency>
<groupId>com.bioserenity.format</groupId>
<artifactId>bios-to-edf-plus</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
</exclusion>
</exclusions>
</dependency>
Source: jackson-module-jaxb-annotations is not compatible with spring boot 3.0
endpoint ‘swagger-ui’ contains invalid characters
Cannot be fixed.
When starting a service, I got the following error from logger org.springframework.boot.actuate.endpoint.EndpointId
:
Endpoint ID 'swagger-ui' contains invalid characters, please migrate to a valid format.
However, the endpoint http://localhost:9121/actuator/swagger-ui can be accessed.
Cannot be fixed right now, see Using springdoc with the Actuator integration causes a deprecation warning.