Compile a Spring Boot Infinispan Application to a GraalVM Native Image

What You Will Learn

How to compile a Spring Boot application that uses the Infinispan Hot Rod client into a GraalVM native image. The resulting native executable starts in milliseconds and uses a fraction of the memory compared to the JVM.

This tutorial builds on the Spring Boot Automatic Protobuf Schema Registration tutorial and adds the configuration needed for GraalVM native image compilation.

Prerequisites

  • Java 21+ with GraalVM (for native compilation)

  • Maven 3.6+

  • Docker or Podman (for running Infinispan Server)

Step 1: Start an Infinispan Server

Start an Infinispan Server using Docker or Podman:

docker run -it --rm -p 11222:11222 -e USER=admin -e PASS=password quay.io/infinispan/server:latest
Tip
You can replace docker with podman in the command above if you use Podman.

Step 2: Add Dependencies

In addition to the standard Spring Boot Infinispan starter, add the GraalVM native image metadata module:

<dependency>
   <groupId>org.infinispan</groupId>
   <artifactId>infinispan-spring-boot4-starter-remote</artifactId>
</dependency>
<dependency>
   <groupId>org.infinispan</groupId>
   <artifactId>infinispan-client-hotrod-graalvm</artifactId>
</dependency>
<dependency>
   <groupId>org.infinispan.protostream</groupId>
   <artifactId>protostream-processor</artifactId>
</dependency>

The infinispan-client-hotrod-graalvm artifact provides META-INF/native-image/ metadata that registers the reflection entries and resource includes needed by the Hot Rod client at native image build time.

Step 3: Define Protobuf Entities and Schema

Define your Protobuf entities and schema exactly as you would for a JVM application:

@Proto
@Indexed
public record Character(UUID id, @Text String name, @Text String bio, Archetype archetype) {

}
@ProtoSchema(schemaPackageName = "tutorial",
      includeClasses = {Character.class, Archetype.class},
      dependsOn = {
            org.infinispan.protostream.types.java.CommonTypes.class
      })
public interface AppSchema extends GeneratedSchema {
}

Step 4: Register the Schema Bean for Native Mode

The Infinispan Spring Boot starter normally discovers GeneratedSchema implementations by scanning the classpath at runtime. This classpath scanning does not work in a native image because all classes are compiled ahead-of-time into the binary.

To work around this, explicitly register the generated schema implementation as a Spring bean:

@Configuration
@ImportRuntimeHints(NativeConfiguration.InfinispanRuntimeHints.class)
public class NativeConfiguration {

   @Bean
   GeneratedSchema appSchema() {
      return new AppSchemaImpl();
   }

   static class InfinispanRuntimeHints implements RuntimeHintsRegistrar {

      @Override
      public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
         hints.resources().registerPattern("*.csv");
      }
   }
}

Key points:

  • The @Bean method returns the annotation-processor-generated AppSchemaImpl. The starter’s auto-configuration picks it up via dependency injection and registers it with the RemoteCacheManager.

  • The RuntimeHintsRegistrar ensures that application-specific resource files loaded at runtime (such as the CSV data file) are included in the native image.

Step 5: Configure the Native Maven Profile

Since this project does not use spring-boot-starter-parent as its Maven parent, the native build profile must be configured manually. Add the following profile to your pom.xml:

<profiles>
   <profile>
      <id>native</id>
      <build>
         <plugins>
            <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <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>
               <extensions>true</extensions>
               <executions>
                  <execution>
                     <id>add-reachability-metadata</id>
                     <goals>
                        <goal>add-reachability-metadata</goal>
                     </goals>
                  </execution>
               </executions>
               <configuration>
                  <buildArgs>
                     <buildArg>-H:+AddAllCharsets</buildArg>
                  </buildArgs>
                  <fallback>false</fallback>
               </configuration>
            </plugin>
         </plugins>
      </build>
   </profile>
</profiles>

This profile configures:

  • Spring Boot AOT processing (process-aot): pre-computes dependency injection, auto-configuration, and @ConfigurationProperties bindings at build time, eliminating the need for reflection at runtime.

  • GraalVM Native Maven Plugin: compiles the AOT-processed application into a native executable. The add-reachability-metadata goal downloads third-party native image metadata from the GraalVM reachability metadata repository.

  • -H:+AddAllCharsets: includes all character set support, required by the Infinispan client for certain encoding operations.

  • fallback=false: ensures a true native image is built; the build fails rather than silently falling back to a JVM-based executable.

Step 6: Build and Run in JVM Mode

First verify that the application works on the JVM:

mvn spring-boot:run

Then test the REST endpoints:

curl http://localhost:8080/characters/0
curl "http://localhost:8080/characters/query?term=Felix"

Step 7: Build the Native Image

Build the native executable using the native profile:

mvn -Pnative native:compile
Note
Native image compilation requires significantly more time and memory than a regular build. Expect 2-5 minutes depending on your machine. Ensure at least 4GB of RAM is available for the build process.

Step 8: Run the Native Executable

Run the compiled native executable:

./target/infinispan-spring-boot-native-cache-remote

You should see the application start in well under a second. The same REST endpoints are available:

curl http://localhost:8080/characters/0
curl "http://localhost:8080/characters/query?term=Felix"

How It Works

Three layers work together to make the native image functional:

Layer What it covers

Spring Boot AOT (process-aot)

All Spring annotations, dependency injection wiring, @ConfigurationProperties, auto-configuration evaluation

infinispan-client-hotrod-graalvm

Hot Rod client internals: SASL authentication providers (Elytron), consistent hashing, event listeners, ProtoStream .proto resource files

NativeConfiguration (this tutorial)

Application-specific: GeneratedSchema bean registration (workaround for classpath scanning), application resource files

What’s Next