# 02 — AOT processing, and why `package` alone doesn't build the image [← Previous: 01 — versions and setup](01-versions-and-setup.md) | [Back to README →](../README.md) | [Next: 03 — the reflection trap →](03-the-reflection-trap.md) ## What Spring's AOT processing actually generates `mvn spring-boot:process-aot` (bound to the `package` phase automatically, no profile needed) is what makes a Spring Boot native image possible at all -- it runs the application's startup logic once, at build time, and writes down what it learns as plain Java source instead of leaving it to be rediscovered by reflection at every future startup. On this project it produced, in `target/spring-aot/main/`: - **95 generated `.java` source files** -- one `*__BeanDefinitions.java` per auto-configuration class and application bean, including [`HelloController__BeanDefinitions.java`](../src/main/java/com/ankurm/graalvmdemo/HelloController.java) and `ReportController__BeanDefinitions.java`, plus a `GraalvmDemoApplication__ApplicationContextInitializer.java` that builds the whole `ApplicationContext` as ordinary Java calls instead of classpath scanning. - **`META-INF/native-image/com.ankurm/graalvm-native-images/reachability-metadata.json`** -- the unified reflection/resource/proxy configuration format GraalVM now uses (a single file, not the older separate `reflect-config.json` / `resource-config.json` / `proxy-config.json` trio). - **`native-image.properties`** -- build arguments the plugin feeds straight to `native-image`. ## Why `mvn -Pnative package` alone does not produce a binary Reflected in this exact setup, not assumed: running `mvn -Pnative -DskipTests package` completes in under nine seconds and its log contains `add-reachability-metadata`, `process-aot`, `jar`, and `repackage` -- and nothing else. Grepping that log for `native-image` or `Generating '` returns zero matches. `mvn help:effective-pom -Pnative` confirms why: the `native` profile this project (and a default Spring Initializr project) declares binds *no* execution of its own to any phase -- it only adds the plugin so its own `add-reachability-metadata` goal, bound in the plugin's own metadata, participates in the build. The goal that actually invokes the GraalVM compiler, `native:compile`, has to be run explicitly: ``` mvn -Pnative -DskipTests native:compile ``` which matches Spring's own current documentation for local (non-Docker) native builds. The `./mvnw clean package -Pnative` command in tutorials and in the earlier version of this project's post does not do this -- confirmed by testing it directly, not by reading the plugin's changelog. Full transcripts of both runs, side by side: [docs/output/06-package-does-not-compile-native.txt](output/06-package-does-not-compile-native.txt) and [docs/output/02-native-build.txt](output/02-native-build.txt). ## The build itself, measured On this project's 2 vCPU / 8 GB build machine, `native-image` analysis found 20,126 reachable types across 29,154 fields and 91,938 methods, registered 7,495 types for reflection, and finished in 4 minutes 14 seconds to 4 minutes 19 seconds across repeated runs, peaking at 4.36 GB resident memory during compilation. The resulting executable is 93 MB, versus a 22 MB jar that still needs a JRE installed alongside it to run at all. Full transcript: [docs/output/02-native-build.txt](output/02-native-build.txt).