Building Spring Boot Native Applications

Spring team is developing spring native module which provides support for compiling Spring applications to native executables using the GraalVM native-image compiler.

Native applications provide following advantages compared to JVM based applications

  • Faster starup time
  • Instant peak performance
  • Reduced memory usage

Required software

  • Spring Boot 2.5.6
  • Graal VM 11 (Community Edition)
  • Maven 3.8+
  • Docker

Setting up the project

To create a project, go to https://start.spring.io and select following spring version and dependencies.

  • SpringBoot version 2.5.6
  • spring-boot-starter-web
  • spring-boot-starter-data-jpa
  • h2
  • spring-native
Note
1) Spring Native 0.10.5 only supports Spring Boot 2.5.6
2) Native images can be created with Java 8 and 11 versions only
3) Java17 version is not supported for native image creation as of writing this blog post.
4) Examples in this blog post are tested with only Java 11 version on Windows machine.

Warning
native-image generation needs a lot of RAM, you should have a machine with at least 16GB of RAM to try out the example.

Download the project, extract and import it into your favourite IDE.

Building Native Image

There are 2 main ways to build a Spring Boot native application

i) With Build Packs

Adding Spring Native module dependency adds all the required plugins and configurations to pom.xml file by spring starter project.

You can observe following module in dependency

                <dependency>
			<groupId>org.springframework.experimental</groupId>
			<artifactId>spring-native</artifactId>
			<version>${spring-native.version}</version>
		</dependency>Code language: Java (java)

In plugins section

<build>
   <plugins>
      <plugin>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-maven-plugin</artifactId>
         <configuration>
            <classifier>${repackage.classifier}</classifier>
            <image>
               <builder>paketobuildpacks/builder:tiny</builder>
               <env>
                  <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
               </env>
            </image>
         </configuration>
      </plugin>
      <plugin>
         <groupId>org.springframework.experimental</groupId>        
            <artifactId>spring-aot-maven-plugin</artifactId>        
            <version>${spring-native.version}</version>
         <executions>
            <execution>
               <id>test-generate</id>
               <goals>
                  <goal>test-generate</goal>
               </goals>
            </execution>
            <execution>
               <id>generate</id>
               <goals>
                  <goal>generate</goal>
               </goals>
            </execution>
         </executions>
      </plugin>
      <plugin>
         <groupId>org.hibernate.orm.tooling</groupId>
         <artifactId>hibernate-enhance-maven-plugin</artifactId>
         <version>${hibernate.version}</version>
         <executions>
            <execution>
               <id>enhance</id>
               <goals>
                  <goal>enhance</goal>
               </goals>
               <configuration>
                  <failOnError>true</failOnError>
                  <enableLazyInitialization>true</enableLazyInitialization>
                  <enableDirtyTracking>true</enableDirtyTracking>
                  <enableAssociationManagement>true</enableAssociationManagement>
                  <enableExtendedEnhancement>false</enableExtendedEnhancement>
               </configuration>
            </execution>
         </executions>
      </plugin>
   </plugins>
</build>Code language: Java (java)

Spring AOT PlugIn : The Spring AOT plugin performs ahead-of-time transformations required to improve native image compatibility and footprint.

Hibernate Enhance Plugin : The goal of the Hibernate Enhance plugin is to instrument the JPA entity bytecode in order to improve the effectiveness and efficiency of the associated data access operations.

The Native application can be built with the following command.

mvn spring-boot:build-imageCode language: Bash (bash)

The above command creates a Linux container to build the native application using the GraalVM native image compiler. By default, the container image is installed locally.

Run the native application

docker run --rm -p 8080:8080 springboot-native-image:0.0.1-SNAPSHOTCode language: Bash (bash)

Now , you should be able to test the application with Rest client.

Note
Native Image creation is very time taking process. It might take up to 10 min. to create the image depending on your system processing power.
Tip
On Windows, make sure to enable the Use the WSL 2 based engine (if WSL2 is installed on your computer) for better performance in Docker during image creationdocker

The Paketo Java Native Image Buildpack internally uses GraalVM to build native image.

By default, GraalVM versions will be upgraded automatically by Buildpacks to the latest release. 

If we want to explicitly configure Spring Boot Maven or Gradle plugins with a specific version of java-native-image buildpack which will freeze GraalVM version, For example, if you want to force using GraalVM 21.2.0, you can configure like below in spring boot maven plugin

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <!-- ... -->
        <image>
            <buildpacks>
                <buildpack>gcr.io/paketo-buildpacks/java-native-image:5.5.0</buildpack>
            </buildpacks>
        </image>
    </configuration>
</plugin>Code language: Java (java)

You can visit the link to know the version mapping between GraalVm and Java Native Image Buildpack Version.

ii) With GraalVM native build tools

We need to install the Gra

A number of prerequisites are required before installing the GraalVM native-image compiler.

i) Download and Install GraalVM CE 21.2.0 (Java 11) version from here based on your operating system

ii) Set the Graal VM as the default JVM on your system.

On Linux

Instructions for changing default Java provider depends on Linux distribution.

For Ubuntu instructions, visit this link

For Manjaro/Arch instructions, visit this link

On Windows

Set JAVA_HOME to GraalVM installation directory

and PATH to GraalVM installation bin directory

iii) Run the below command to bring native-image extensions to the JDK

On Linux

${GRAALVM_HOME}/bin/gu install native-imageCode language: Bash (bash)

On Windows

%JAVA_HOME%\bin\gu install native-imageCode language: Bash (bash)

Make sure that, you are using GraalVm as default JVM with Maven by running below command

mvn --versionCode language: Bash (bash)

In pom.xml add the following plugin configuration under native profile.

                     <plugin>
						<groupId>org.springframework.boot</groupId>
						<artifactId>spring-boot-maven-plugin</artifactId>
						<configuration>
							<classifier>exec</classifier>
						</configuration>
					</plugin>Code language: XL (xl)

Now run the below command to build native application

On Linux

mvn -Pnative -DskipTests packageCode language: Bash (bash)

On Windows

Download and Install Visual Studio 2019 Visual C++ Build Tools and Visual Studio 2019 community edition.

Now search for x86_x64 cross tools command prompt for VS 2019 and open the command prompt by clicking on it.

Now navigate to project folder and run following command

mvn -Pnative -DskipTests packageCode language: Bash (bash)

The above command will create exe file in the target folder

Run the native application

./target/springboot-native-image.exeCode language: Bash (bash)
Native application

Let’s test native application with a Rest client

Comparing the startup times

Let’s compare the JVM (jar) and native application start-up times.

JVM (Jar) version start-up time is 4.907 seconds and Native application start-up time is 0.727 seconds

Troubleshooting

I faced the following issues while building the native applications.

Image build request failed with exit status 137

You might see error message like below when the system does not have enough memory to build the native image.

Try to use the system with at least 1GB of RAM.

Error: Image build request failed with exit status 137

Builder lifecycle ‘creator’ failed with status code 145

This is a generic error triggered by Docker and forwarded by Spring Boot Buildpacks support. native-image command has likely failed, so check the error messages in the output. If you can’t find anything, check if that’s not an out of memory error as described above.

You can download the source code for blog post from GitHub

You might be also interested in

Similar Posts