or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

admin-jmx.mdansi-support.mdaot-native-image.mdapplication-info.mdavailability.mdbootstrap.mdbootstrapping.mdbuilder.mdcloud-platform.mdconfiguration-annotations.mdconfiguration-data.mdconfiguration-properties.mdconversion.mddiagnostics.mdenvironment-property-sources.mdindex.mdjson-support.mdlifecycle-events.mdlogging.mdorigin-tracking.mdresource-loading.mdretry-support.mdssl-tls.mdstartup-metrics.mdsupport.mdsystem-utilities.mdtask-execution.mdthreading.mdutilities.mdvalidation.mdweb-support.md
tile.json

aot-native-image.mddocs/

AOT Processing and Native Image Support

Spring Boot's Ahead-of-Time (AOT) processing enables compilation to GraalVM native images for faster startup, lower memory footprint, and improved runtime performance.

Package Information

  • Package: org.springframework.boot
  • Since: 3.0.0 (SpringApplicationAotProcessor), 3.2.6 (AotInitializerNotFoundException)
  • Purpose: AOT processing infrastructure for native image generation

Core Imports

import org.springframework.boot.SpringApplicationAotProcessor;
import org.springframework.boot.AotInitializerNotFoundException;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

Overview

AOT processing analyzes the Spring application at build time to generate:

  • Optimized code: Pre-computed bean definitions and configurations
  • Runtime hints: GraalVM reflection, proxy, and resource configuration
  • Native image metadata: Everything needed for native compilation

This eliminates the need for runtime reflection and enables instant startup times (typically <100ms vs 2-3 seconds for JVM).

Capabilities

AOT Processor

The main entry point for AOT processing during build time.

package org.springframework.boot;

import org.springframework.context.aot.ContextAotProcessor;
import org.springframework.context.support.GenericApplicationContext;

/**
 * Entry point for AOT processing of a SpringApplication.
 * For internal use only - typically invoked by build tools (Maven/Gradle plugins).
 *
 * @since 3.0.0
 */
public class SpringApplicationAotProcessor extends ContextAotProcessor {

    /**
     * Create a new processor for the specified application and settings.
     *
     * @param application the application main class
     * @param settings the general AOT processor settings (output paths, etc.)
     * @param applicationArgs the arguments to provide to the main method
     */
    public SpringApplicationAotProcessor(Class<?> application,
                                        Settings settings,
                                        String[] applicationArgs);

    /**
     * Process the application and generate AOT artifacts.
     * Prepares application context, performs AOT processing, and writes output.
     */
    public void process() throws Exception;

    /**
     * Main method for command-line invocation (used by build plugins).
     *
     * Usage: SpringApplicationAotProcessor <applicationMainClass> <sourceOutput>
     *        <resourceOutput> <classOutput> <groupId> <artifactId> [originalArgs...]
     */
    public static void main(String[] args) throws Exception;
}

AOT Initialization Exception

Exception thrown when the AOT-generated initializer class cannot be found at runtime.

package org.springframework.boot;

/**
 * Exception thrown when the AOT initializer couldn't be found.
 * This typically occurs when running with spring.aot.enabled=true but the
 * AOT processing phase was not executed or failed.
 *
 * @since 3.2.6
 */
public class AotInitializerNotFoundException extends RuntimeException {

    /**
     * Create exception with main class and initializer class name.
     *
     * @param mainClass the application main class
     * @param initializerClassName the expected initializer class name
     */
    public AotInitializerNotFoundException(Class<?> mainClass, String initializerClassName);

    /**
     * Get the application main class that failed to find its initializer.
     *
     * @return the main class
     */
    public Class<?> getMainClass();
}

Native Image Build Process

Maven Configuration

<project>
    <properties>
        <java.version>21</java.version>
        <graalvm.version>23.0.1</graalvm.version>
    </properties>

    <build>
        <plugins>
            <!-- Spring Boot Maven Plugin with AOT support -->
            <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>

            <!-- Native Build Tools Plugin -->
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <configuration>
                    <classesDirectory>${project.build.outputDirectory}</classesDirectory>
                    <metadataRepository>
                        <enabled>true</enabled>
                    </metadataRepository>
                </configuration>
                <executions>
                    <execution>
                        <id>add-reachability-metadata</id>
                        <goals>
                            <goal>add-reachability-metadata</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Gradle Configuration

plugins {
    id 'java'
    id 'org.springframework.boot' version '4.0.0'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'org.graalvm.buildtools.native' version '0.10.0'
}

java {
    sourceCompatibility = '21'
}

// Native image configuration
graalvmNative {
    binaries {
        main {
            imageName = 'myapp'
            mainClass = 'com.example.MyApplication'
            buildArgs.add('--verbose')
            buildArgs.add('-H:+ReportExceptionStackTraces')
        }
    }
    metadataRepository {
        enabled = true
    }
}

// Enable AOT processing
tasks.named('processAot') {
    args = []
}

Building Native Image

# Maven: Build native image
./mvnw -Pnative native:compile

# Maven: Build native image using Buildpacks
./mvnw spring-boot:build-image

# Gradle: Build native image
./gradlew nativeCompile

# Gradle: Build native image using Buildpacks
./gradlew bootBuildImage

# Run the native executable
./target/myapp

# Or on Windows
./target/myapp.exe

Custom Runtime Hints

Provide hints to GraalVM about reflection, resources, proxies, and JNI.

import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportRuntimeHints;

@SpringBootApplication
@ImportRuntimeHints(MyApplication.MyRuntimeHints.class)
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    // Register custom runtime hints
    static class MyRuntimeHints implements RuntimeHintsRegistrar {

        @Override
        public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
            // Reflection hints for classes loaded dynamically
            hints.reflection()
                .registerType(MyDynamicallyLoadedClass.class, builder ->
                    builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS,
                                      MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));

            // Resource hints for files loaded at runtime
            hints.resources()
                .registerPattern("config/*.properties")
                .registerPattern("data/*.json");

            // Proxy hints for dynamic proxies
            hints.proxies()
                .registerJdkProxy(MyInterface.class, Serializable.class);

            // JNI hints for native method access
            hints.jni()
                .registerType(MyNativeClass.class, builder ->
                    builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));

            // Serialization hints
            hints.serialization()
                .registerType(MySerializableClass.class);
        }
    }
}

Testing Native Images

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.aot.DisabledInAotMode;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension.class)
class MyApplicationTests {

    @Test
    void contextLoads() {
        // Test application starts successfully
    }

    @Test
    @DisabledInAotMode // Skip this test in AOT/native mode
    void testWithReflection() {
        // Test that uses reflection not supported in native image
    }
}

Run tests against native image:

# Maven: Test native image
./mvnw -PnativeTest test

# Gradle: Test native image
./gradlew nativeTest

Troubleshooting

AotInitializerNotFoundException

Problem: Application fails to start with AotInitializerNotFoundException.

Startup with AOT mode enabled failed: AOT initializer __ApplicationContextInitializer
could not be found

Causes:

  1. AOT processing was not run during build
  2. AOT processing failed but build continued
  3. spring.aot.enabled=true but no AOT artifacts present

Solution:

# Ensure AOT processing runs during build
./mvnw clean package -Pnative

# Check AOT output directory exists
ls -la target/spring-aot/main/sources

# Verify initializer class was generated
find target/spring-aot -name "*__ApplicationContextInitializer.java"

# Review build logs for AOT processing errors
./mvnw clean package -X | grep "process-aot"

Missing Reflection Configuration

Problem: ClassNotFoundException or NoSuchMethodException at runtime.

Solution: Add runtime hints for dynamically loaded classes:

@ImportRuntimeHints(MyRuntimeHints.class)
@SpringBootApplication
public class MyApplication {

    static class MyRuntimeHints implements RuntimeHintsRegistrar {
        @Override
        public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
            // Register all classes from a package
            hints.reflection()
                .registerType(TypeReference.of("com.example.MyClass"),
                    MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS,
                    MemberCategory.INVOKE_PUBLIC_METHODS);
        }
    }
}

Missing Resource Files

Problem: FileNotFoundException for resources loaded at runtime.

Solution: Register resource patterns:

hints.resources()
    .registerPattern("application*.properties")
    .registerPattern("templates/*.html")
    .registerPattern("static/**")
    .registerResourceBundle("messages");

Dynamic Proxy Issues

Problem: Proxy creation fails at runtime.

Solution: Register proxy interfaces:

hints.proxies()
    .registerJdkProxy(MyInterface.class)
    .registerJdkProxy(FirstInterface.class, SecondInterface.class);

Native Image Compatibility

Supported Features ✅

  • Spring Boot auto-configuration
  • Spring MVC and WebFlux
  • Spring Data JPA, MongoDB, Redis
  • Spring Security
  • Actuator endpoints
  • Configuration properties
  • Profile-specific configuration
  • Logging (Logback, Log4j2)
  • Scheduled tasks
  • Event listeners
  • ApplicationRunner/CommandLineRunner

Limited Support ⚠️

  • JDBC drivers (require explicit hints)
  • CGLIB proxies (prefer JDK proxies or proxyTargetClass=false)
  • JMX (disabled by default in native images)
  • Dynamic class loading
  • Groovy, Kotlin scripting
  • AspectJ load-time weaving

Not Supported ❌

  • Runtime bean definition registration (after AOT)
  • Dynamic proxy creation (without hints)
  • Reflection without hints
  • JVM-specific tools (JConsole, VisualVM)
  • Java agents

Best Practices

  1. Test Early: Run native tests frequently during development
  2. Provide Hints: Register runtime hints for any dynamic behavior
  3. Use Starter Dependencies: Spring Boot starters include AOT metadata
  4. Avoid Reflection: Use constructor injection and final fields
  5. Profile Native Mode: Use @Profile("native") for native-specific beans
  6. Monitor Build Times: Native compilation takes 2-5 minutes - cache appropriately
  7. Check Compatibility: Verify all dependencies support GraalVM native image

Production Deployment

Container Image

FROM ghcr.io/graalvm/native-image:ol8-java21 AS builder

WORKDIR /build

# Copy project files
COPY pom.xml .
COPY src ./src

# Build native image
RUN ./mvnw package -Pnative -DskipTests

# Runtime stage - minimal image
FROM gcr.io/distroless/base-debian11

COPY --from=builder /build/target/myapp /app/myapp

ENTRYPOINT ["/app/myapp"]

Performance Characteristics

MetricJVM ModeNative Mode
Startup Time2-3s<100ms
Memory Usage (RSS)200-500MB50-100MB
First Request LatencyHighLow
Steady-State ThroughputHighComparable
Build Time<1min3-5min
Image Size150-250MB80-120MB

Environment Variables

# Enable AOT mode explicitly
export SPRING_AOT_ENABLED=true

# Native image specific configuration
export SPRING_NATIVE_REMOVE_UNUSED_AUTOCONFIG=true
export SPRING_NATIVE_REMOVE_YAML_SUPPORT=false

See Also

  • GraalVM Native Image
  • Spring Boot Native Documentation
  • Spring AOT Documentation
  • GraalVM Reachability Metadata Repository