skip to content

Hint Categories

Hints are grouped by kind — reflection, resources, proxies, serialization, bundles, JNI — each mapping to a native-image configuration file. Knowing the categories tells you which one a given failure needs.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

6

What is Spring's RuntimeHints, and what are the main categories of hints it exposes?

level: juniorimportance: must knowfreq 55%

answer

  1. Closed-world analysis drops the unseen
  2. RuntimeHints = programmatic native config
  3. reflection/resources/proxies/serialization/jni
  4. Each sub-API → one GraalVM JSON file
  5. Missing hint fails at runtime, not build

basics

~10 s

RuntimeHints is a Spring object that records what a native image needs kept at runtime. It groups hints into sub-APIs: reflection, resources, JDK proxies, serialization, and JNI — each feeding a GraalVM config file.

solid answer

~30 s

When you compile a Spring app to a GraalVM native image, GraalVM does closed-world static analysis and drops anything it can't prove is used. Reflection, resource loading, dynamic proxies, and serialization defeat that analysis, so you must declare them. Spring's `RuntimeHints` class is the programmatic model for those declarations. It exposes sub-APIs via accessor methods: `reflection()` → `ReflectionHints`, `resources()` → `ResourceHints` (which also covers resource bundles), `proxies()` → `ProxyHints` (JDK dynamic proxies), `serialization()` → `SerializationHints`, and `jni()` → `ReflectionHints` for JNI. During AOT processing Spring translates these into GraalVM's native-image config JSON files (reflect-config.json, resource-config.json, proxy-config.json, serialization-config.json, jni-config.json).

code

java · 14 lines
java
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.MemberCategory;

public class MyHints implements RuntimeHintsRegistrar {
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        // Each sub-API targets one native-image config file
        hints.reflection().registerType(MyDto.class, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
        hints.resources().registerPattern("templates/*.html");
        hints.proxies().registerJdkProxy(MyCallback.class);
        hints.serialization().registerType(MyDto.class);
    }
}

go deeper

for a junior

Know that native image removes unused code and RuntimeHints tells it what dynamic things to keep, grouped into a few categories.

for a middle

Should name the sub-APIs (reflection/resources/proxies/serialization/jni) and the file each maps to, and know missing hints fail at runtime.

for a senior

Explain the AOT build phase, who contributes hints, and how to verify them with RuntimeHintsPredicates.

for a principal

Discuss closed-world tradeoffs, the reachability-metadata consolidation, and strategy for third-party libraries lacking reachability metadata.

## The problem GraalVM native image performs **closed-world static analysis** at build time: it walks all reachable code from `main`, and anything it cannot statically prove is reachable is removed from the final binary. This makes images small and fast to start, but it breaks any behavior that is decided *dynamically* at runtime rather than visible in the call graph: - **Reflection** — `Class.forName`, `clazz.getDeclaredMethod(...).invoke(...)` - **Resources** — `getResourceAsStream("/messages.properties")` (the file isn't code, so it's not bundled) - **JDK dynamic proxies** — `Proxy.newProxyInstance(...)` for a set of interfaces - **Serialization** — Java serialization needs reflective access to a type's fields - **JNI** — native code calling back into Java via reflection-like lookups GraalVM lets you *declare* these needs via JSON config files placed under `META-INF/native-image/<group>/<artifact>/`. ## Spring's abstraction: `RuntimeHints` Writing raw JSON by hand is tedious and error-prone, and Spring itself needs to contribute thousands of such entries for its own infrastructure. So Spring models the whole thing as an in-memory Java API: `org.springframework.aot.hint.RuntimeHints`. It is a container of typed sub-registries, each reached through an accessor: | Accessor | Returns | GraalVM file | |---|---|---| | `reflection()` | `ReflectionHints` | `reflect-config.json` | | `resources()` | `ResourceHints` (patterns **and** resource bundles) | `resource-config.json` | | `proxies()` | `ProxyHints` (JDK dynamic proxies) | `proxy-config.json` | | `serialization()` | `SerializationHints` | `serialization-config.json` | | `jni()` | `ReflectionHints` (separate instance, JNI-scoped) | `jni-config.json` | (Newer GraalVM consolidates these into a single `reachability-metadata.json`, but the conceptual categories are the same.) ## Who fills it in During Spring's **AOT processing** (which runs at build time — e.g. via the `process-aot` Gradle/Maven goal or the Spring Boot AOT plugin), Spring runs its `BeanFactoryInitializationAotProcessor`s and `BeanRegistrationAotProcessor`s, plus any `RuntimeHintsRegistrar` you register. All of them write into one shared `RuntimeHints`. Finally a `NativeConfigurationWriter` serializes it to the JSON files, which GraalVM's `native-image` tool then consumes. ## When you write hints yourself Spring and its starters cover most of the framework automatically. You add hints when **your own code** does something dynamic that Spring can't see: reflectively loading a class by name, reading a bundled `.properties`/template, proxying your own interface, serializing a DTO, etc. You express these through the matching sub-API rather than editing JSON. ## Gotcha Missing a hint does **not** fail the build — it fails at *runtime* in the native image with errors like `ClassNotFoundException`, `MissingResourceException`, or a proxy/serialization failure. That's why the hints model and testing them (`RuntimeHintsPredicates`) matter.

  • If you forget a required hint, when and how do you find out?
    Not at build time. The native image starts fine but fails at runtime when the dynamic path executes — e.g. ClassNotFoundException, NoSuchMethodException, MissingResourceException, or a serialization/proxy error. You catch these with integration tests, GraalVM's tracing agent, or Spring's RuntimeHintsPredicates in unit tests.
  • Does Spring make you write hints for @Autowired beans or @Configuration classes?
    No. Spring's own AOT processors generate those automatically during process-aot. You only write hints for dynamic behavior in your own code that the framework can't infer — reflective class loading by name, custom resource reads, hand-rolled proxies, etc.

saying these in an interview costs you the question

  • Saying hints are needed for every class in the app
  • Claiming a missing hint fails the native build (it fails at runtime)
  • Confusing RuntimeHints with runtime configuration/properties

context

open as a page

How do you register reflection hints with ReflectionHints, and what does MemberCategory control?

level: middleimportance: must knowfreq 60%

basics

~10 s

Use hints.reflection().registerType(MyType.class, category...). MemberCategory selects which members become reflectively accessible — e.g. declared constructors, public/declared methods, or fields. It maps to reflect-config.json.

open as a page

How do ResourceHints handle static resources versus resource bundles in a native image?

level: middleimportance: should knowfreq 45%

basics

~10 s

hints.resources().registerPattern("...") bundles matching files (templates, properties) into the image. hints.resources().registerResourceBundle("messages") includes an i18n ResourceBundle base name. Both go into resource-config.json.

open as a page

When and how do you register JDK dynamic proxy hints with ProxyHints?

level: seniorimportance: should knowfreq 42%

basics

~10 s

Use hints.proxies().registerJdkProxy(A.class, B.class...), passing the exact ordered set of interfaces the proxy implements. Needed when code creates a JDK Proxy at runtime. It maps to proxy-config.json.

open as a page

What do SerializationHints and JNI hints do, and how do they differ from ordinary reflection hints?

level: seniorimportance: should knowfreq 30%

basics

~10 s

hints.serialization().registerType(T.class) enables Java serialization of a type in native (→ serialization-config.json). hints.jni() returns a ReflectionHints whose entries go to jni-config.json, for native code calling back into Java. Both are separate from ordinary reflect-config.json reflection.

open as a page

How are RuntimeHints contributed to a Spring AOT build, and how do the sub-APIs map to GraalVM config files?

level: principalimportance: should knowfreq 38%

basics

~10 s

Implement RuntimeHintsRegistrar and register it via @ImportRuntimeHints or META-INF/spring/aot.factories. During AOT processing Spring collects all hints and a config writer emits reflect-/resource-/proxy-/serialization-/jni-config.json for GraalVM.

open as a page