skip to content

How do you trigger AOT processing in a Maven and a Gradle Spring Boot build, and how is it enabled at runtime?

level: middleimportance: should knowfreq 35%

answer

  1. Maven: process-aot goal
  2. Gradle: processAot task
  3. SpringApplicationAotProcessor runs the app
  4. runtime flag spring.aot.enabled
  5. auto-on in native, opt-in on JVM

basics

~20 s

In Maven you run the spring-boot:process-aot goal; in Gradle you run the processAot task. Both generate the AOT sources. At runtime the generated code is used only when spring.aot.enabled=true, which is automatic inside a native image.

solid answer

~40 s

The Spring Boot Maven plugin provides the **process-aot** goal (and **process-test-aot**), and the Gradle plugin adds the **processAot** task (and **processTestAot**). Running them executes `SpringApplicationAotProcessor`, generating the initializer + RuntimeHints and compiling them into the build output. For native builds these steps are wired in automatically (e.g. `mvn -Pnative native:compile`, or the GraalVM Gradle plugin's `nativeCompile`). Crucially, generating AOT code doesn't change JVM runtime behavior by itself — the generated initializer is only picked up when **spring.aot.enabled=true**. Inside a native image that flag is on by default; on the JVM you set it explicitly (system property or env) to get the AOT startup benefits. So there are two knobs: a *build-time* task to generate, and a *runtime* flag to consume.

go deeper

for a junior

Name the Maven goal and Gradle task and that native builds run them automatically.

for a middle

Separate the build-time trigger from the runtime spring.aot.enabled flag; know where output lands.

for a senior

Discuss build-time context refresh side effects and the JVM-AOT test loop before native.

for a principal

Own the CI story: profile pinning, side-effect-free init, hint validation on JVM before slow native compile.

## Build-time triggers ### Maven (`spring-boot-maven-plugin`) - Goal **`process-aot`** — generates AOT code for `main`. - Goal **`process-test-aot`** — generates AOT code for tests. - Invoked directly: `mvn spring-boot:process-aot`, or automatically when building a native image with the GraalVM `native-maven-plugin` (`mvn -Pnative native:compile`). ### Gradle (Spring Boot Gradle plugin) - Task **`processAot`** — generates AOT code for `main`; wires generated sources into `compileJava`. - Task **`processTestAot`** — the test equivalent. - The GraalVM `org.graalvm.buildtools.native` plugin's `nativeCompile` depends on these. Both ultimately run **`org.springframework.boot.SpringApplicationAotProcessor`**, which starts the app in AOT mode, calls `ApplicationContextAotGenerator`, and writes: - Maven: `target/spring-aot/main/sources`, `.../resources`, `.../classes` - Gradle: `build/generated/aotSources`, `build/generated/aotResources`, `build/generated/aotClasses` plus native-image config under `META-INF/native-image/<group>/<artifact>/`. ## Runtime flag Generated code is inert unless **`spring.aot.enabled=true`**. Then `SpringApplication` (via `AotApplicationContextInitializer`) applies the generated `<App>__ApplicationContextInitializer` instead of scanning. - **Native image:** the flag is set **automatically** — a native image can't scan/reflect freely, so AOT is mandatory. - **Plain JVM:** you opt in, e.g. `java -Dspring.aot.enabled=true -jar app.jar` or `SPRING_AOT_ENABLED=true`. This gives faster startup while still running on the JVM (handy to *test* AOT wiring before building a native image). ## Practical gotchas - Running `process-aot` **starts your application context** at build time (to know the beans). Anything that fails or has side effects during refresh (e.g. connecting to external systems eagerly) can break or slow the build — keep context init side-effect-free. - **Active profiles are fixed at generation time.** Set them when you run `process-aot`; you can't switch profile-conditional beans later at runtime. - Testing on the JVM with `spring.aot.enabled=true` is the recommended way to catch missing hints before the slower native build. - Don't commit or edit the generated sources; they're regenerated each build.

  • Why can running process-aot fail if your context connects to a database on startup?
    Because process-aot actually starts the application context (refreshForAotProcessing) to discover beans. Eager side effects like DB connections execute at build time and can fail or hang the build; init should be side-effect-free.
  • How do you test AOT wiring without building a native image?
    Run the app on the JVM with spring.aot.enabled=true after processAot/process-aot. It uses the generated initializer, surfacing missing hints or AOT-incompatible beans far faster than a native build.

saying these in an interview costs you the question

  • Thinking process-aot only matters for native images and never running it on the JVM.
  • Assuming generating AOT code automatically changes JVM runtime behavior without spring.aot.enabled.
  • Believing active profiles can still be chosen at runtime after AOT.

context