All Java templates

GraalVM + Spring Boot Native

Spring Boot 3 native compilation, AOT processing, and runtime hints.

DevZone Tools1,380 copiesUpdated Apr 3, 2026GraalVMSpring BootSpringJava
# 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 Java templates