SpringBoot 3.0: The Holy Graal

Milos Biljanovic
Better Programming
Published in
5 min readApr 13, 2023

--

Photo by Will van Wingerden on Unsplash

SpringBoot 3.0 (with Spring 6.0) went GA on 24th of November last year. Since I’m back in the Spring world it deserves a proper follow-up on my part.

Here are all the amazing things that were included:

  1. Jakarta EE 9+ and Java 17 baseline
  2. Problem Details RFC
  3. Observability
  4. AOT and GraalVM
  5. Client for Web API

Jakarta EE 9+ upgrade

So in order to move to Spring Boot 3.0, the minimum requirement is JDK 17. JDK 17 is the LTS version and has been around for almost one year and a half, so there was time to upgrade unless you are from heavy legacy with older versions of Java. If you haven’t upgraded already, here’s a guide that might help you.

Regarding Jakarta, package names were changed from javax.* to jakarta.* so you will need to rename them.

One more interesting thing is that these changes didn’t come out of anywhere. The move to upgrade the baseline was announced two years ago. For a more detailed timeline, you can listen to Stephane Nicoll and Brian Clozel's first ten minutes of the talk. I would highly recommend listening to the whole talk if you have time.

Problem Details RFC

So you know how you would handle custom exceptions with @ControllerAdvice where you would have your custom messages wrapped inside a ResponseEntity. It is a tedious process and you still need to figure out what your message should look like. Imagine if the message was standardized but you could still extend it. Welcome to the Problem Detail RFC. SpringBoot 3.0 provides a ProblemDetail class to do the trick.

With simple controller advice below:

You can create a ProblemDetail which when returned from your app looks like:

The great thing is that if you need extra fields you can extend ProblemDetail.

For example, if you wanted to return a current balance that is not sufficient to make a purchase you could have that extra field added.

Here is a short explanation for each field from the RFC:

Observability

Obviously, when you write code you would like to know in production when something stops working or is slow for any reason. You don’t really want your users to tell you that. Observability is a separate and huge topic, but Spring Boot 3.0 has stepped up in that direction.

It already had Actuator and you could use Micrometer (somewhat separately). In Spring Boot 3.0 Micrometer is integrated within Spring Boot.

The unified Observation API supports metrics and tracing.

Below is an example of a simple UserController:

In this example, we are creating a simple metric users.find.

The method observe is actually a timer that will measure the execution time of whatever is passed inside.

You can check the metric through Actuator. It looks like this by default:

Here you can see execution time measurements with available tags like an error and our IllegalArgumentException when the user is not found.

There is a lot more to cover here, but let’s just say that you don’t even have to declare it like this on each Controller, you can set it up in a way that each Controller is automatically observed.

AOT and GraalVM

What is GraalVM?

It initially started as a project to have one VM that would love them (languages) all. The idea was to have one VM that supports all languages and has the same infrastructure. The idea was around language diversity that existed back then and now.

With multi-language support, you can basically have a Java app within GraalVM that is embedded in Node.js.

Other than multi-language support, other goals are to have programs/code run more efficiently and to make devs more productive.

What are we going to have here in our example is a native standalone binary compiled with GraalVM Ahead of Time compiler that is small in size and super fast in starting up. With Ahead of Time compiler things at start-up time are faster due to being already done in the compilation phase.

What is AOT in Spring Boot?

AOT stands for ahead-of-time compilation. It is just an extra step when building the app.

Steps

  1. Compile code
  2. Build an optimized app ready for GraalVM

Step two actually means doing a lot of stuff that was previously done in runtime, eg. evaluating conditions, process configurations, etc.

For a detailed explanation of what is built in AOT step, you can check the already mentioned talk.

In order to try it yourself:

  1. Clone project from github
  2. With Maven you can run mvn -Pnative spring-boot:build-image
  3. Once the image is created, you can run the docker image with
    docker run --rm -p 8080:8080 docker.io/library/demo:0.0.1-SNAPSHOT

The building image step takes some time, but once done, running the app is instant. Other than being fast, the memory footprint is really small keeping in mind that it contains everything you need.

You can read more about how AOT works here.

The last thing to add here is that some things are not supported by default and need some manual configuration to work. I expect this to improve over time. One of those things is Client for Web API.

Client for Web API

Last but not least is Client for calling rest endpoints.

This looks pretty awesome if you ask me. Reminds very much of how we use JpaRepositories.

In order to turn this into Graal native image we need some manual configuration due to how UsersClient is created. It uses a proxy for creating the implementation of the interface at runtime whereas GraalVM is very restrictive about what can be done at runtime. Basically, we need to create a hint for AOT compilation.

Unfortunately, even with the hint changes I couldn’t manage it to work due to AOT compile failing for some (non-mine) log back Logger usages. I’ve left the code if someone wants to check it out.

Even if this worked, I think that these manual hints are tedious and need to be done automatically which I believe will be added at some point.

Photo by Raimond Klavins on Unsplash

Here are the backend and client GitHub links for you to try out the cool features of Spring Boot 3.0.

Happy coding!

--

--