How to build Spring Boot native code

This tutorial will teach you how to turn your Spring Boot Java applications in a native executable using the Spring Native project.

The Spring Native projects enables the compilation of Spring applications to native applications using the GraalVM native-image compiler. The advantages of a native applications are several. For example:

  • Faster start up
  • Overall better performance
  • Reduced Memory Usage
  • Reduced application size (dead code branches will be removed at build time)

Getting your machine ready

You can build Spring Boot native executable by adding a set of required extensions and plugins to your pom.xml. Before that, you need to install some tools.

Firstly, we will add to tool to manage Software Development Kits. On a Linux/Mac machine, you can install SDKMAN. You can install SDKMAN as follows:

curl -s "https://get.sdkman.io" | bash

Next, let your terminal reload the configuration:

source "$HOME/.sdkman/bin/sdkman-init.sh"

To verify that the installation was successful, run the following command:

$ sdk version
SDKMAN 5.13.1

Next, install and enable GraalVM native-image distribution, such as Bellsoft Liberica NIK:

$ sdk install java 21.3.0.r11-nik

$ sdk use java 21.3.0.r11-nik

Finally, install the native-image using the tool ‘gu’:

gu install native-image

Building your first Spring Boot Native executable

Your machine is now ready to compile Spring Boot applications to native executables. To build your first application, we recommend checking the sample projects which are available on GitHub.

For example, we can run the commandlinerunner demo (https://github.com/spring-projects-experimental/spring-native/tree/main/samples/commandlinerunner):

cd commandlinerunner

Firstly, let’s check the pom.xml to see which additions are required to build a native executable:

<project
		xmlns="http://maven.apache.org/POM/4.0.0"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.experimental</groupId>
		<artifactId>spring-native-sample-parent</artifactId>
		<version>0.11.1-SNAPSHOT</version>
		<relativePath>../maven-parent/pom.xml</relativePath>
	</parent>
	<groupId>com.example</groupId>
	<artifactId>commandlinerunner</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.experimental</groupId>
			<artifactId>spring-native</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<!-- Check out the parent POM for the plugins configuration -->
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.experimental</groupId>
				<artifactId>spring-aot-maven-plugin</artifactId>
				<configuration>
					<removeYamlSupport>true</removeYamlSupport>
					<removeSpelSupport>true</removeSpelSupport>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

As you can see, we need to include the org.springframework.experimental:spring-native which provides native configuration APIs. Also, we need to include the spring-aot-maven-plugin plugin which performs ahead-of-time transformations required to improve native image compatibility and footprint.

Behind the hoods, this project references also a native Maven profile that you need to build the native application. We will see this more in detail in the next section.

Right now, to build the native application, you can use the build.sh script which you will find at the root of each project:

./build.sh 
=== Building commandlinerunner sample ===
Packaging commandlinerunner with Maven (native)
SUCCESS
Testing commandlinerunner
SUCCESS
Build memory: 5.08GB
Image build time: 77.3s
RSS memory: 28.7M
Image size: 24.3M
Startup time: 0.018 (JVM running for 0.019)
Lines of reflective config: 836

The native application is now available under the target folder:

$ target/commandlinerunner

Creating a new Spring Boot native application

If you want to bootstrap a new Spring Boot application, the simplest option is to use the Spring Boot initializr and add as a dependency Spring Native:

spring boot native

As you can see, besides the dependency discussed in our commandlinerunner example, there is also a native profile that you can use to build native applications:

<profile>
	<id>native</id>
	<properties>
		<repackage.classifier>exec</repackage.classifier>
		<native-buildtools.version>0.9.8</native-buildtools.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.junit.platform</groupId>
			<artifactId>junit-platform-launcher</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.graalvm.buildtools</groupId>
				<artifactId>native-maven-plugin</artifactId>
				<version>${native-buildtools.version}</version>
				<extensions>true</extensions>
				<executions>
					<execution>
						<id>test-native</id>
						<phase>test</phase>
						<goals>
							<goal>test</goal>
						</goals>
					</execution>
					<execution>
						<id>build-native</id>
						<phase>package</phase>
						<goals>
							<goal>build</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</profile>

To build the native application, simply use the native profile:

 mvn -Pnative -DskipTests package

You will find the native executable under the target folder of your Maven project.

Conclusion

In this tutorial you have learnt how to configure your machine to build Spring Boot native applications. Then, we have run one example application from the sample repository. Finally, we have created a new application from scratch using the Spring Boot initializer