Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Microsoft – NPI EA (cat = Baeldung)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Microsoft – NPI EA (cat= Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, you can get started over on the documentation page.

And, you can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat=Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag=Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – All Access – NPI EA (cat= Spring)
announcement - icon

All Access is finally out, with all of my Spring courses. Learn JUnit is out as well, and Learn Maven is coming fast. And, of course, quite a bit more affordable. Finally.

>> GET THE COURSE
Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – LambdaTest – NPI EA (cat=Testing)
announcement - icon

End-to-end testing is a very useful method to make sure that your application works as intended. This highlights issues in the overall functionality of the software, that the unit and integration test stages may miss.

Playwright is an easy-to-use, but powerful tool that automates end-to-end testing, and supports all modern browsers and platforms.

When coupled with LambdaTest (an AI-powered cloud-based test execution platform) it can be further scaled to run the Playwright scripts in parallel across 3000+ browser and device combinations:

>> Automated End-to-End Testing With Playwright

Course – Spring Sale 2025 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 25% off until 26th May, 2025:

>> EXPLORE ACCESS NOW

Course – Spring Sale 2025 – NPI (cat=Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 25% off until 26th May, 2025:

>> EXPLORE ACCESS NOW

1. Overview

In this article, we’ll learn about native images and how to create native images from Spring Boot applications and GraalVM’s Native Image builder. We refer to Spring Boot 3, but we’ll address discrepancies with Spring Boot 2 at the end of the article.

2. Native Images

A native image is a technology to build Java code to a standalone executable. This executable includes the application classes, classes from its dependencies, runtime library classes, and statically linked native code from JDK. The JVM is packaged into the native image, so there’s no need for any Java Runtime Environment at the target system, but the build artifact is platform-dependent. So we’ll need one build per supported target system, which will be easier when we use container technologies like Docker, where we can build a container as a target system that can be deployed to any Docker runtime.

2.1. GraalVM and Native Image Builder

The General Recursive Applicative and Algorithmic Language Virtual Machine (Graal VM) is a high-performance JDK distribution written for Java and other JVM languages, along with support for JavaScript, Ruby, Python, and several other languages. It provides a Native Image builder –  a tool to build native code from Java applications and package it together with the VM into a standalone executable. It is officially supported by the Spring Boot Maven and Gradle Plugin with a few exceptions (the worst is that Mockito does not support native tests at the moment).

2.2. Special Features

There are two typical features that we meet when building native images.

Ahead-Of-Time (AOT) Compilation is the process of compiling high-level Java code into native executable code. Usually, this is made by the JVM’s Just-in-time compiler (JIT) at runtime, which allows for observation and optimization while executing the application. This advantage is lost in the case of AOT compilation.

Typically, before AOT compilation, there can optionally be a separate step called AOT processing, i.e. collecting metadata from the code and providing them to the AOT compiler. The division into these 2 steps makes sense because AOT processing can be framework specific, while the AOT compiler is more generic. The following picture gives an overview:

Overview: Native Build Steps

Another specialty of the Java platform is its extensibility on the target system by just putting JARs into the classpath. Because of reflection and annotation scanning on startup, we then get extended behavior in the application.

Unfortunately, this slows down startup time and does not bring any benefit, especially for cloud-native applications, where even the server runtime and the Java base classes are packaged into the JAR. So, we dispense with this feature and can then build the application using Closed World Optimization.

Both features reduce the amount of work needed to be performed at runtime.

2.3. Advantages

Native images provide various advantages, like an instant startup and reduced memory consumption. They can be packaged into a lightweight container image for faster and more efficient deployment, and they present a reduced attack surface.

2.4. Limitations

Because of the Closed World Optimization, there are some limitations, that we have to be aware of when writing application code and using frameworks. Shortly:

  • Class initializers can be executed at build time for faster startup and better peak performance. But we have to be aware that this may break some assumptions in the code, e.g., when loading a file that then has to be available at build time.
  • Reflection and Dynamic Proxies are expensive at runtime and therefore optimized at build time under the Closed World assumption. We can use it without restriction in class initializers when executed at build time. Any other usage must be announced to the AOT compiler, which the Native Image builder tries to reach by performing static code analysis. If this fails, we have to provide this information, e.g., by a configuration file.
  • The same applies to all technologies based on reflection, like JNI and Serialization.
  • In addition, the Native Image builder provides its own native interface that is much simpler than JNI and with lower overhead.
  • For native image builds, bytecode is not available at runtime anymore, so Debugging and Monitoring with tools targeted to the JVMTI is not possible. We then have to use native debuggers and monitoring tools.

Concerning Spring Boot, we have to be aware that features like profiles, conditional beans, and .enable properties are not fully supported at runtime anymore. If we use profiles, they have to be specified at build time.

3. Basic Setup

Before we can build native images, we have to install the tools.

3.1. GraalVM and Native Image

First, we install the current version of GraalVM and the native-image builder following the installation instructions. (Version 22.3 is required by Spring Boot) We should make sure that the installation directory is available via the GRAALVM_HOME environment variable and that “<GRAALVM_HOME>/bin” is added to the PATH variable.

3.2. Native Compiler

During the build, the Native Image builder calls the platform-specific native compiler. So, we need this native compiler, following the “Prerequisite” instructions for our platform. This will make the build platform-dependent. We have to be aware that running the build is only possible within the platform-specific command line. For example, running the build on Windows using Git Bash won’t work. We need to use the Windows command line instead.

3.3. Docker

As a prerequisite, we’ll make sure to install Docker, required later to run native images. The Spring Boot Maven and Gradle Plugins use Paketo Tiny Builder to build a container.

4. Configure and Build Project with Spring Boot

Using the Native Build Feature with Spring Boot is quite simple. We create our project, e.g., by using Spring Initializr and adding the application code. Then, to build a native image with GraalVM’s Native Image builder, we need to extend our build with the Maven or Gradle plugin provided by GraalVM itself.

4.1. Maven

The Spring Boot Maven Plugin has goals for AOT processing (i.e., not AOT compiling itself, but collecting metadata for the AOT compiler, e.g., registering the usage of reflection in the code) and for building an OCI image that can be run with Docker. We could invoke these goals directly:

mvn spring-boot:process-aot
mvn spring-boot:process-test-aot
mvn spring-boot:build-image

We don’t need to do so because the Spring Boot parent POM defines a native profile that binds these goals to the build. We need to build with this activated profile:

mvn clean package -Pnative

If we also want to execute native tests, there’s a second profile that we could activate:

mvn clean package -Pnative,nativeTest

If we want to build a native image, we have to add the corresponding goal of the native-maven-plugin. We could therefore define a native profile too. Because this plugin is managed by the parent POM, we can leave the version number:

<profiles>
    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.graalvm.buildtools</groupId>
                    <artifactId>native-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>build-native</id>
                            <goals>
                                <goal>compile-no-fork</goal>
                            </goals>
                            <phase>package</phase>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

Currently, Mockito is not supported in native test execution. So we could exclude Mocking tests or simply skip native testing by adding this to our POM:

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <configuration>
                    <skipNativeTests>true</skipNativeTests>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

4.2. Using Spring Boot Without Parent POM

If we cannot inherit from Spring Boot Parent POM but use it as an import-scoped dependency, we have to configure plugins and profiles by ourselves. Then, we have to add this to our POM:

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <version>${native-build-tools-plugin.version}</version>
                <extensions>true</extensions>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<profiles>
    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <image>
                            <builder>paketobuildpacks/builder:tiny</builder>
                            <env>
                                <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
                            </env>
                        </image>
                    </configuration>
                    <executions>
                        <execution>
                            <id>process-aot</id>
                            <goals>
                                <goal>process-aot</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.graalvm.buildtools</groupId>
                    <artifactId>native-maven-plugin</artifactId>
                    <configuration>
                        <classesDirectory>${project.build.outputDirectory}</classesDirectory>
                        <metadataRepository>
                            <enabled>true</enabled>
                        </metadataRepository>
                        <requiredVersion>22.3</requiredVersion>
                    </configuration>
                    <executions>
                        <execution>
                            <id>add-reachability-metadata</id>
                            <goals>
                                <goal>add-reachability-metadata</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
    <profile>
        <id>nativeTest</id>
        <dependencies>
            <dependency>
                <groupId>org.junit.platform</groupId>
                <artifactId>junit-platform-launcher</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>process-test-aot</id>
                            <goals>
                                <goal>process-test-aot</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.graalvm.buildtools</groupId>
                    <artifactId>native-maven-plugin</artifactId>
                    <configuration>
                        <classesDirectory>${project.build.outputDirectory}</classesDirectory>
                        <metadataRepository>
                            <enabled>true</enabled>
                        </metadataRepository>
                        <requiredVersion>22.3</requiredVersion>
                    </configuration>
                    <executions>
                        <execution>
                            <id>native-test</id>
                            <goals>
                                <goal>test</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>
<properties>
    <native-build-tools-plugin.version>0.9.17</native-build-tools-plugin.version>
</properties>

4.3. Gradle

The Spring Boot Gradle Plugin provides tasks for AOT processing (i.e., not AOT compiling itself, but collecting metadata for the AOT compiler, e.g., registering the usage of reflection in the code) and for building an OCI image that can be run with Docker:

gradle processAot
gradle processTestAot
gradle bootBuildImage

If we want to build a native image, we have to add the Gradle plugin for GraalVM Native Image building:

plugins {
    // ...
    id 'org.graalvm.buildtools.native' version '0.9.17'
}

Then, we can run the tests and build the project by invoking

gradle nativeTest
gradle nativeCompile

Currently, Mockito is not supported in native test execution. So we could exclude Mocking tests or skip native testing by configuring the graalvmNative extension as follows:

graalvmNative {
    testSupport = false
}

5. Extend the Native Image Build Configuration

As already mentioned, we have to register each usage of reflection, classpath scanning, dynamic proxies, etc., for the AOT compiler. Because the built-in native support of Spring is a very young feature, not all Spring modules currently have built-in support, so we currently need to add this by ourselves. This could be done by creating the build configuration manually. Still, it is easier to use the provided interface of Spring Boot so that both the Maven and Gradle Plugins can use our code during AOT processing to generate the build configuration.

One possibility to specify the additional native configuration is Native Hints. So, let’s see two examples of built-in support currently missing and how to add it to our application to make it work.

5.1. Sample: Jackson’s PropertyNamingStrategy

In an MVC web application, each return value of a REST controller method is serialized by Jackson, naming each property automatically to a JSON element. We can globally influence name mapping by configuring Jackson’s PropertyNamingStrategy in the application properties file:

spring.jacksonproperty-naming-strategy=SNAKE_CASE

SNAKE_CASE is the name of a static member of the PropertyNamingStrategies type. Unfortunately, this member is resolved by reflection. So the AOT compiler needs to know about that, otherwise, we’ll get an error message:

Caused by: java.lang.IllegalArgumentException: Constant named 'SNAKE_CASE' not found
  at org.springframework.util.Assert.notNull(Assert.java:219) ~[na:na]
  at org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
        $Jackson2ObjectMapperBuilderCustomizerConfiguration
        $StandardJackson2ObjectMapperBuilderCustomizer.configurePropertyNamingStrategyField(JacksonAutoConfiguration.java:287) ~[spring-features.exe:na]

To reach this, we then can implement and register the RuntimeHintsRegistrar in a simple way like this:

@Configuration
@ImportRuntimeHints(JacksonRuntimeHints.PropertyNamingStrategyRegistrar.class)
public class JacksonRuntimeHints {

    static class PropertyNamingStrategyRegistrar implements RuntimeHintsRegistrar {

        @Override
        public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
            try {
                hints
                  .reflection()
                  .registerField(PropertyNamingStrategies.class.getDeclaredField("SNAKE_CASE"));
            } catch (NoSuchFieldException e) {
                // ...
            }
        }
    }

}

Note: A pull request to solve this issue within Spring Boot was already merged since version 3.0.0-RC2, so it works with Spring Boot 3 out of the box.

5.2. Sample: GraphQL Schema Files

If we want to implement a GraphQL API, we need to create a schema file and locate it under “classpath:/graphql/*.graphqls”, where it is automatically detected by Springs GraphQL autoconfiguration. This is done via Classpath Scanning, as well as the welcome page of the integrated GraphiQL test client. So to work correctly within the native executable, the AOT compiler needs to know about this. We can register this the same way:

@ImportRuntimeHints(GraphQlRuntimeHints.GraphQlResourcesRegistrar.class)
@Configuration
public class GraphQlRuntimeHints {

    static class GraphQlResourcesRegistrar implements RuntimeHintsRegistrar {

        @Override
        public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
            hints.resources()
              .registerPattern("graphql/**/")
              .registerPattern("graphiql/index.html");
        }
    }

}

The Spring GraphQL team is already working on this, so we might get this built-in in a future version.

6. Writing Tests

To test the RuntimeHintsRegistrar implementation, we don’t even need to run a Spring Boot test, we can create a simple JUnit test like this:

@Test
void shouldRegisterSnakeCasePropertyNamingStrategy() {
    // arrange
    final var hints = new RuntimeHints();
    final var expectSnakeCaseHint = RuntimeHintsPredicates
      .reflection()
      .onField(PropertyNamingStrategies.class, "SNAKE_CASE");
    // act
    new JacksonRuntimeHints.PropertyNamingStrategyRegistrar()
      .registerHints(hints, getClass().getClassLoader());
    // assert
    assertThat(expectSnakeCaseHint).accepts(hints);
}

If we want to test it with an integration test, we can check the Jackson ObjectMapper to have the correct configuration:

@SpringBootTest
class JacksonAutoConfigurationIntegrationTest {

    @Autowired
    ObjectMapper mapper;

    @Test
    void shouldUseSnakeCasePropertyNamingStrategy() {
        assertThat(mapper.getPropertyNamingStrategy())
          .isSameAs(PropertyNamingStrategies.SNAKE_CASE);
    }

}

To test it with the native mode, we have to run a native test:

# Maven
mvn clean package -Pnative,nativeTest
# Gradle
gradle nativeTest

If we need to provide test-specific AOT support for Spring Boot Tests, we could implement a TestRuntimeHintsRegistrar or a  TestExecutionListener using the AotTestExecutionListener interface. We can find details in the official documentation.

7. Spring Boot 2

Spring 6 and Spring Boot 3 have made a big step concerning native image builds. But with the previous major version, this is also possible. We just need to know that there is no built-in support yet, i.e., there was a supplementary Spring Native initiative that dealt with this topic. So, we have to include and configure this in our project manually. For AOT processing, there was a separate Maven and Gradle plugin, which isn’t merged into the Spring Boot plugin. And, of course, integrated libraries did not provide native support to the same extent as they do now (and will do even more so in the future).

7.1. Spring Native Dependency

First, we have to add the Maven Dependency for Spring Native:

<dependency>
    <groupId>org.springframework.experimental</groupId>
    <artifactId>spring-native</artifactId>
    <version>0.12.1</version>
</dependency>

However, for a Gradle project, Spring Native is automatically added by the Spring AOT plugin.

We should note that each Spring Native version only supports a specific Spring Boot version – for example, Spring Native 0.12.1 only supports Spring Boot 2.7.1. So, we should make sure to use the compatible Spring Boot Maven dependencies in our pom.xml.

7.2. Buildpacks

To build an OCI image, we need to explicitly configure a build pack.

With Maven, we’ll require the spring-boot-maven-plugin with native image configuration using the Paketo Java buildpacks:

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <image>
                        <builder>paketobuildpacks/builder:tiny</builder>
                        <env>
                            <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
                        </env>
                    </image>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

Here, we’ll use the tiny builder out of the various available builders, like base and full to build a native image. Also, we enabled the buildpack by providing the true value to the BP_NATIVE_IMAGE environment variable.

Similarly, when using Gradle, we can add the tiny builder along with the BP_NATIVE_IMAGE environment variable to the build.gradle file:

bootBuildImage {
    builder = "paketobuildpacks/builder:tiny"
    environment = [
        "BP_NATIVE_IMAGE" : "true"
    ]
}

7.3. Spring AOT Plugin

Next, we’ll need to add the Spring AOT plugin that performs ahead-of-time transformations helpful in improving the footprint and compatibility of the native image.

So, let’s add the latest spring-aot-maven-plugin Maven dependency to our pom.xml:

<plugin>
    <groupId>org.springframework.experimental</groupId>
    <artifactId>spring-aot-maven-plugin</artifactId>
    <version>0.12.1</version>
    <executions>
        <execution>
            <id>generate</id>
            <goals>
                <goal>generate</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Similarly, for a Gradle project, we can add the latest org.springframework.experimental.aot dependency in the build.gradle file:

plugins {
    id 'org.springframework.experimental.aot' version '0.10.0'
}

Also, as we noted earlier, this will add the Spring Native dependency to the Gradle project automatically.

The Spring AOT plugin provides several options to determine the source generation. For example, options like removeYamlSupport and removeJmxSupport remove the Spring Boot Yaml and Spring Boot JMX support, respectively.

7.4. Build and Run Image

That’s it! we’re ready to build a native image of our Spring Boot project by using the Maven command:

$ mvn spring-boot:build-image

7.5. Native Image Builds

Next, we’ll add a profile named native with build support of a few plugins like native-maven-plugin and spring-boot-maven-plugin:

<profiles>
    <profile>
        <id>native</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.graalvm.buildtools</groupId>
                    <artifactId>native-maven-plugin</artifactId>
                    <version>0.9.17</version>
                    <executions>
                        <execution>
                            <id>build-native</id>
                            <goals>
                                <goal>build</goal>
                            </goals>
                            <phase>package</phase>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <classifier>exec</classifier>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

This profile will invoke the native-image compiler from the build during the package phase.

However, when using Gradle, we’ll add the latest org.graalvm.buildtools.native plugin to the build.gradle file:

plugins {
    id 'org.graalvm.buildtools.native' version '0.9.17'
}

That’s it! We’re ready to build our native image by providing the native profile in the Maven package command:

mvn clean package -Pnative

8. Conclusion

In this tutorial, we explored Native Image builds with Spring Boot and GraalVM’s native build tools. We learned about Spring’s built-in native support.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

Partner – Microsoft – NPI EA (cat = Spring Boot)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

Partner – Orkes – NPI EA (cat = Spring)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

Partner – Orkes – NPI EA (tag = Microservices)
announcement - icon

Modern software architecture is often broken. Slow delivery leads to missed opportunities, innovation is stalled due to architectural complexities, and engineering resources are exceedingly expensive.

Orkes is the leading workflow orchestration platform built to enable teams to transform the way they develop, connect, and deploy applications, microservices, AI agents, and more.

With Orkes Conductor managed through Orkes Cloud, developers can focus on building mission critical applications without worrying about infrastructure maintenance to meet goals and, simply put, taking new products live faster and reducing total cost of ownership.

Try a 14-Day Free Trial of Orkes Conductor today.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Course – Spring Sale 2025 – NPI EA (cat= Baeldung)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 25% off until 26th May, 2025:

>> EXPLORE ACCESS NOW

Course – Spring Sale 2025 – NPI (All)
announcement - icon

Yes, we're now running our Spring Sale. All Courses are 25% off until 26th May, 2025:

>> EXPLORE ACCESS NOW

Partner – Microsoft – NPI (cat=Spring)
announcement - icon

Azure Container Apps is a fully managed serverless container service that enables you to build and deploy modern, cloud-native Java applications and microservices at scale. It offers a simplified developer experience while providing the flexibility and portability of containers.

Of course, Azure Container Apps has really solid support for our ecosystem, from a number of build options, managed Java components, native metrics, dynamic logger, and quite a bit more.

To learn more about Java features on Azure Container Apps, visit the documentation page.

You can also ask questions and leave feedback on the Azure Container Apps GitHub page.

eBook Jackson – NPI EA – 3 (cat = Jackson)