skip to content

Beyond reflection, what do the resources, proxies, and serialization sub-registrars of RuntimeHints do, and when would you use each?

level: middleimportance: should knowfreq 30%

answer

  1. resources: registerPattern / registerResourceBundle
  2. proxies: registerJdkProxy(interfaces...)
  3. serialization: registerType (Java Serializable)
  4. Jackson JSON => reflection, not serialization
  5. Spring auto-covers @Transactional/@Async proxies

basics

~10 s

hints.resources() keeps classpath files/bundles you load by name; hints.proxies() declares JDK dynamic proxies for interfaces; hints.serialization() keeps types you (de)serialize with Java serialization. Use each when native analysis can't see that dynamic usage.

solid answer

~30 s

RuntimeHints has four dynamic-feature sub-registrars beyond reflection. hints.resources().registerPattern("messages/*.properties") keeps classpath resources that you load by name (getResourceAsStream) — patterns aren't statically visible, so they'd be stripped; registerResourceBundle handles i18n bundles. hints.proxies().registerJdkProxy(MyInterface.class) declares a JDK dynamic proxy — GraalVM must generate the proxy class at build time, so you list the exact interface set; Spring already covers @Transactional/@Async proxies it detects, but a hand-rolled Proxy.newProxyInstance needs a hint. hints.serialization().registerType(MyDto.class) keeps a type usable with Java's Serializable mechanism. You use each when your code (or a library) triggers that dynamic behavior in a way Spring's automatic AOT contributions don't already detect.

code

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

public class DynamicFeatureHints implements RuntimeHintsRegistrar {
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader cl) {
        // Resources loaded by name/pattern at runtime.
        hints.resources().registerPattern("config/*.json");
        hints.resources().registerResourceBundle("messages");

        // A JDK dynamic proxy we create ourselves.
        hints.proxies().registerJdkProxy(com.example.AuditPort.class);

        // A type we persist via Java serialization.
        hints.serialization().registerType(com.example.SessionData.class);
    }
}

go deeper

for a junior

Should know there are more categories than reflection (resources, proxies).

for a middle

Should map each sub-registrar to its dynamic feature and give a use case.

for a senior

Should catch the Jackson-uses-reflection trap and know Spring auto-covers framework proxies.

for a principal

Should reason about minimizing hint surface and which contributions Spring already provides vs. what the app must add.

`RuntimeHints` exposes four sub-registrars beyond `reflection()`, each mapping to a GraalVM dynamic-feature config. **1. Resources — `hints.resources()` (`ResourceHints`).** Native image only bundles classpath resources it's told about. If you load a file by name/pattern at runtime (`getResourceAsStream`, `ClassPathResource`, `@Value("classpath:...")` read dynamically), declare it: ```java hints.resources().registerPattern("config/*.json"); hints.resources().registerPattern("templates/email.html"); ``` Patterns use `*`/`**` globbing. For i18n: ```java hints.resources().registerResourceBundle("messages"); ``` Without this, the resource is absent in the binary and you get a null stream / missing-resource error. Note Spring Boot already registers many standard resources (e.g. `application.properties`), so only add non-standard ones. **2. Proxies — `hints.proxies()` (`ProxyHints`).** JDK dynamic proxies (`java.lang.reflect.Proxy`) are generated at runtime on the JVM, but native image must generate them at *build* time, so it needs the exact ordered set of interfaces: ```java hints.proxies().registerJdkProxy(MyService.class, org.springframework.aop.SpringProxy.class); ``` Spring's AOT already contributes proxies for framework features it detects — `@Transactional`, `@Async`, AOP advisors, `@ConfigurationProperties` interfaces, repository interfaces. You add hints only for proxies your own code creates via `Proxy.newProxyInstance`, or interface-based proxies a library doesn't self-declare. (CGLIB/class-based proxies are handled through reflection hints instead, and Spring's AOT prefers generating source over class proxies where it can.) **3. Serialization — `hints.serialization()` (`SerializationHints`).** For Java's built-in serialization (`Serializable`, `ObjectOutputStream`), the types must be registered: ```java hints.serialization().registerType(SessionData.class); ``` This is comparatively rare in modern apps (JSON via Jackson uses *reflection* hints, not serialization hints) — it matters for HTTP session persistence, RMI, or libraries using JDK serialization. **4. JNI — `hints.jni()`.** Rarely needed in typical Spring apps; declares types reachable via the Java Native Interface. **Choosing.** Ask what dynamic mechanism your code uses: reading a file by name → resources; `Proxy.newProxyInstance` on interfaces → proxies; `ObjectOutputStream`/`Serializable` → serialization; `Class.forName`/`Method.invoke`/field access → reflection. **Gotchas.** - Jackson JSON binding needs *reflection* hints (or `@RegisterReflectionForBinding`), not serialization hints — a very common mix-up. - Proxy interface order matters and must match exactly what the runtime requests. - Resource patterns are matched against the classpath; a typo silently yields no match and a runtime miss. - All four run at build time; none of this metadata is consulted on the JVM.

  • You serialize a DTO to JSON with Jackson in a native image and it fails. Which sub-registrar fixes it?
    Reflection, not serialization. Jackson reads/writes via reflection over the type's fields/getters, so you need reflection hints (or the @RegisterReflectionForBinding convenience). SerializationHints is only for Java's Serializable/ObjectOutputStream mechanism.
  • Do you need a proxy hint for a @Transactional service?
    Usually no — Spring's AOT processing already contributes the proxy/reflection metadata for framework-managed proxies like @Transactional and @Async. You add proxy hints for proxies your own code (or an unaware library) creates.

saying these in an interview costs you the question

  • Using serialization hints for Jackson/JSON binding
  • Thinking resource files are automatically bundled without a pattern
  • Manually adding proxy hints for @Transactional (already covered)
  • Ignoring that JDK proxy interface order/set must match exactly

context