The Spring “family” is built around the Spring framework, one of the most popular Java frameworks in existence, with Spring Boot as a flagship. First released 18 years ago, it keeps growing and rejuvenating. While newer solutions appear, Spring stays on top. It is still a lightweight, secure, easily customizable, and flexible programming environment that has become a standard helper in modern Java development.
But today, we would like to focus on the newest addition to the Spring “family” ー Spring Native, soon to be released. Now in beta, it has caught the attention of developers worldwide and is available for testing.
Note that our own Liberica Native Image Kit is fully compatible with Spring Boot. You can read all about it below or turn to our engineers for free advice.
So what is Spring Native?
Spring Native is a version of Spring Framework with built-in support for compiling applications into native images with GraalVM. It allows running Spring-based code on any system without a conventional Java Virtual Machine with almost instant startup, instant peak performance, and low memory consumption. Using AOT (ahead-of-time) compiler, Spring Native generates a small image containing the OS layer, dependencies necessary for running the code, and bits of OpenJDK and Spring, all packed into an executable file.
In the end, it trades longer build times and, possibly, less runtime optimizations for faster startup, lower footprint and simpler container structure. That fits well for cloud deployment, management systems such as Kubernetes and even a serverless approach.
Spring is getting cooler!
So why does it matter?
Glad you asked! There are actually several reasons:
- Spring is catching up with competitors, having solved one of the most critical issues of the framework. The GraalVM native image support was in Quarkus from the start, and Micronaut added it quite some time ago. Now Spring is also compatible with it and can compete in this matter.
- In the Cloud era, the size of microservices is more crucial than ever. It saves time AND money. The Spring applications tend to get bloated with time, and Spring Native solves this problem.
- Although still experimental, Spring Native is available on the official site of the developer to play with, and the images created with it already display faster startup times and lower memory footprints than Spring-generated jars.
- With the future Spring framework version release based on Java 17, you can expect many changes under the hood that will enhance the already excellent performance of Spring Native.
- It took so long to implement this technology because of Spring Boot and GraalVM’s very different behavior. The AOT compilation is not a great fit for Spring’s very dynamic, pluggable runtime behavior. This issue is solved with a bootstrap code generator (available with maven or gradle plugin). This is a huge milestone that allows us to enjoy the best of both worlds.
Spring Native + Liberica Native Image Kit: Let’s try it!
As we already mentioned, Spring Native is available for testing. While we can expect more improvements with the final release of Spring based on JDK 17, there is still a way to experiment with the performance of the feature.
So let’s turn a jar into the native image! We will use the petclinic project as an example, our own Liberica JDK 11 as runtime, and Liberica Native Image Kit as a tool.
The easiest way to start with Spring Native is to create an artifact using Spring Initializr or study examples at https://github.com/spring-projects-experimental/spring-native/tree/main/samples. In our case, we will use the petclinic-jdbc
application.
If you take a look at the main and parent pom.xml contents in a Maven project, there are few notable features:
- Dependencies include
spring-native plugin
(currentlyorg.springframework.experimental:spring-native:0.10.3-SNAPSHOT
). - Build section includes
spring-aot-maven-plugin
andorg.graalvm.buildtools:native-maven-plugin
. - There is a separate ‘native’ profile with its own
native-maven-plugin
configuration. org.springframework.boot:spring-boot-maven-plugin
is configured to use a paketo buildpacks builder (paketobuildpacks/builder:tiny
) which is inBP_NATIVE_IMAGE=true
mode by default.
There are multiple options on how to build and run the application.
In any case, we will need a JDK to run Maven. I have Liberica JDK installed as a package on my Linux system, so the JAVA_HOME
part in the command line is set accordingly.
Classical JAR
Let’s start with building a JAR file.
JAVA_HOME=/usr/lib/jvm/bellsoft-java11-amd64/ ~/apache-maven-3.8.2/bin/mvn install
It results in a 24 MB target/petclinic-jdbc-0.0.1-SNAPSHOT.jar. You can start it as usual:
java -jar target/petclinic-jdbc-0.0.1-SNAPSHOT.jar
Startup time on my laptop is about 2.4 seconds.
Default Native Image container
Let us build a container.
Note that if you use Windows or macOS, you need to ensure that memory allocated to Docker is at least 8 GB.
Run this command:
JAVA_HOME=/usr/lib/jvm/bellsoft-java11-amd64/ ~/apache-maven-3.8.2/bin/mvn spring-boot:build-image
We got a container image that you can launch with
docker run -it docker.io/library/petclinic-jdbc:0.0.1-SNAPSHOT
As it is native image-based, the startup time is about only 0.1 seconds. A massive difference from 2.4 seconds!
To make our work even more convenient, let’s get a fully functional application with a selected database by using docker-compose
and docker-compose.yml
configuration:
docker-compose up
This method also turns into classical layered container image assembly by changing BP_NATIVE_IMAGE
to false
(JVM and .jar inside).
Native Image
Paketo buildpack described in the previous section uses a specific version of GraalVM and native-image
. For example, in the current version it is GraalVM 21.0.0.2 (Java Version 1.8.0_282-b07). But we can use that “native” profile mentioned earlier to build the native image any way we want. For example, let’s use the Liberica Native Image Kit based on GraalVM 21.2.0 and Liberica JDK 11.0.12.
First, download Liberica Native Image Kit Core. The Core version is best suited for Java native images, as it contains Liberica VM and native image (derived from GraalVM) without additional languages at the start. Then choose your platform and packaging. In my case, it is Linux x86_64 and .tar.gz, Liberica NIK version is 21.2.0.
Now pass the full path of the installation in JAVA_HOME
and invoke the “native” profile for the build:
JAVA_HOME=$(pwd)/bellsoft-liberica-vm-core-openjdk11-21.2.0 ~/apache-maven-3.8.2/bin/mvn -Pnative install
The process takes about 10 minutes to finish (it uses 8 CPU cores and about 8 GB of RAM). As a result, we get the 118 MB target/petclinic-jdbc
binary. After testing it, we see it starting in just 1/10th of a second ー the very best result we managed to produce today! In this case we used a more recent version of native image tooling, than the default one. But sometimes there is a need to stay on a specific release until all migration issues are resolved, so this method could be used in reverse.
Liberica loves Spring
As you see, even in this experimental stage, Spring and Liberica NIK work pretty well together.
The important thing to mention is that you will always be able to use the previous LTS versions of Liberica NIK if you are not ready to upgrade to the new Java 17. In this article, we discussed all the deprecations and innovations in the newest OpenJDK. If you are wary of backward compatibility, you can still use Liberica JDK 11, as it will get updates up to 2026. For more information, see the support roadmap.
And while at the moment, we still require a lot of customization to make Spring Boot friends with GraalVM, that is about to change. In September, Java 17 will be out, and a new version of Spring Boot is bound to be released a bit later, making creating native images a much smoother process. As for now, we suggest you dive into Spring Boot, test it out and check how your Spring applications run fast like never before!