spring framework


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

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.logging.logback-extensions

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

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();
  }

Source: java - Spring Security exposing AuthenticationManager without WebSecurityConfigurerAdapter - Stack Overflow

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.