GraalVM + Spring Boot Native
Spring Boot 3 native compilation, AOT processing, and runtime hints.
# CLAUDE.md — GraalVM + Spring Boot Native
## Setup
- Spring Boot 3.0+ has first-class native support via Spring AOT.
- Add the GraalVM Native Build Tools:
- Maven: enable the `native` profile in the parent POM (`spring-boot-starter-parent` ships it).
- Gradle: apply `id 'org.graalvm.buildtools.native'`.
- Build:
- Maven: `./mvnw -Pnative native:compile`
- Gradle: `./gradlew nativeCompile`
- Container image build (no GraalVM locally needed):
- Maven: `./mvnw -Pnative spring-boot:build-image`
- Gradle: `./gradlew bootBuildImage`
## Spring AOT
- AOT processing runs at build time. Produces source/config that bypasses runtime reflection.
- Generated artifacts live in `target/spring-aot/` — don't edit, they regenerate.
- AOT also runs in JVM mode (Boot 3+) for faster startup. You get the benefit without going native.
## Runtime hints
- Spring auto-discovers most reflection. For your own custom code, register via `RuntimeHintsRegistrar`:
```java
public class MyHints implements RuntimeHintsRegistrar {
public void registerHints(RuntimeHints hints, ClassLoader cl) {
hints.reflection().registerType(MyDto.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
hints.resources().registerPattern("data/*.json");
}
}
```
- Register via `@ImportRuntimeHints(MyHints.class)` on a `@Configuration` class.
## What works in native
- Spring MVC, WebFlux, Data JPA, Security, Actuator, JDBC. Most starters just work.
- Tested with H2, Postgres, MySQL, MongoDB, Redis, Kafka, RabbitMQ.
- For full status, see [Spring's native compatibility matrix](https://docs.spring.io/spring-boot/reference/packaging/native-image/index.html).
## What doesn't work
- Mock-based testing in native (`@MockBean`) — use it in JVM, integration-test in native.
- DevTools, Spring Boot Loader's reload — JVM-only.
- Runtime bytecode manipulation libraries that don't ship native hints.
## Tracing agent
- For libraries without first-class native support, use the agent during a JVM test run:
```sh
java -agentlib:native-image-agent=config-merge-dir=src/main/resources/META-INF/native-image/ -jar target/app.jar
```
- Run real test scenarios — the agent records reflection / resources used.
## Build optimization
- Build is the bottleneck. Mitigate:
- Cache `target/` and `~/.m2` in CI between runs.
- Use `-march=native` only if the build host matches the deployment host architecture. Otherwise pick a target.
- Build native only on release branches; PRs build JVM-mode (with AOT) for fast feedback.
## Image
- Use distroless or minimal Debian: `FROM gcr.io/distroless/java-base-debian12`.
- Don't bundle the full Java image — native binaries don't need a JVM.
- Buildpacks produce a layered image automatically.
## Performance characteristics
- Cold start: 50–200 ms vs 2–5 s on JVM.
- Memory: ~80 MB vs ~250 MB for a typical Boot app.
- Peak throughput: native is **slower** than JIT-warmed JVM for long-running services. Don't switch for throughput.
## Don't
- Don't go native to "save memory" without measuring. JVM's `MaxRAMPercentage` + container limits often suffice.
- Don't use `@MockBean` in native tests. They depend on bytecode manipulation.
- Don't enable `spring.aot.enabled=false` in production. Lose AOT, lose the speedup.
- Don't ignore tracing-agent output. The reflection config is doing real work.
Other Spring Boot templates
Spring Boot 3 Modern Rules
Boot 3+ defaults: starters, configuration properties, profiles, and structured logging.
Spring Boot REST API
RESTful endpoints, OpenAPI/SpringDoc, error model, and response shaping.
Spring Boot Testing
@SpringBootTest, MockMvc, slice tests, and Testcontainers — fast, real, layered.
Spring Boot Actuator & Observability
Actuator endpoints, Micrometer metrics, structured tracing, and Prometheus.
Spring Boot Deployment
Buildpacks, layered jars, Docker images, and native image with GraalVM.