the tracing agent, and a real reflection-collision trap Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EQNA6DJ9VgCtW6zhCE8Xud
54 lines
3.3 KiB
Markdown
54 lines
3.3 KiB
Markdown
# 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).
|