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 templates
Spring Framework Core Rules
Dependency injection, bean lifecycle, configuration classes, and profiles.
Spring Data JPA
Repositories, derived queries, custom JPQL, projections, and N+1 prevention.
Spring Security Rules
SecurityFilterChain (no WebSecurityConfigurerAdapter), OAuth2 resource server, JWT.
Spring MVC Rules
Controllers, ResponseEntity, validation, exception handling, and content negotiation.
Spring WebFlux (Reactive)
Reactive Spring with Mono/Flux, R2DBC, backpressure, and non-blocking patterns.