skip to content

@Reflective & ReflectiveProcessor

The @Reflective meta-annotation binds a processor that contributes hints for whatever it annotates, which is the machinery under the declarative annotations. Useful when you are building the framework rather than using it.

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

explore

questions

5

How does @RegisterReflectionForBinding build on the @Reflective mechanism, and when would you use it?

level: seniorimportance: must knowfreq 30%

answer

  1. @Reflective(RegisterReflectionForBindingProcessor.class)
  2. delegates to BindingReflectionHintsRegistrar
  3. walks the whole type graph for serialization
  4. for Jackson/JSON DTOs invisible to inference
  5. value/classes aliases; put on @Configuration

basics

~10 s

@RegisterReflectionForBinding is meta-annotated with @Reflective(RegisterReflectionForBindingProcessor.class). For the classes you list, its processor uses BindingReflectionHintsRegistrar to register the reflection hints needed to serialize/deserialize them (e.g. with Jackson) in a native image.

solid answer

~40 s

@RegisterReflectionForBinding is the practical, developer-facing wrapper over the @Reflective engine. It's meta-annotated @Reflective(RegisterReflectionForBindingProcessor.class), so when AOT scans a bean carrying it, that processor runs. Unlike SimpleReflectiveProcessor's surgical single-member hints, the binding processor delegates to BindingReflectionHintsRegistrar, which walks the type graph of each listed class — properties, nested types, record components, enums — and registers the fuller set of reflection hints a serialization library needs (declared fields, invocable constructors and accessor methods). You use it whenever a DTO is bound reflectively but isn't reachable through normal Spring metadata: request/response bodies serialized by Jackson, payloads sent over messaging, WebClient/RestClient body types, etc. You place it on a @Configuration class or a component and list the classes via value/classes. It saves you from writing a manual RuntimeHintsRegistrar for the common serialization case.

code

java · 16 lines
java
// Register serialization/binding hints for DTOs the native image can't infer.
@Configuration
@RegisterReflectionForBinding({ OrderDto.class, CustomerDto.class })
public class BindingHintsConfig {
}

// Equivalent lower-level approach without the annotation:
public class MyHints implements RuntimeHintsRegistrar {
    private final BindingReflectionHintsRegistrar binding =
            new BindingReflectionHintsRegistrar();
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        binding.registerReflectionHints(hints.reflection(),
                OrderDto.class, CustomerDto.class);
    }
}

go deeper

for a junior

Know it registers reflection hints so DTOs serialize correctly in native images.

for a middle

Explain you list classes and place it on a config/bean, and it's for Jackson-style binding.

for a senior

Tie it to @Reflective + RegisterReflectionForBindingProcessor + BindingReflectionHintsRegistrar and contrast with SimpleReflectiveProcessor's narrow hints.

for a principal

Compare with @RegisterReflection, manual RuntimeHintsRegistrar/@ImportRuntimeHints, discuss bean-scope reachability and native-test verification strategy.

## The problem it targets In a native image, libraries like **Jackson** serialize/deserialize your DTOs by reflecting over their fields, getters, setters and constructors. If those members aren't declared as reflection hints, binding silently fails (missing properties, `InvalidDefinitionException`, empty objects). Spring registers hints automatically for types it can *see* (e.g. `@RestController` return types are often inferred), but many binding types — payloads reached only generically, types behind `List<T>`/`Map<K,V>`, messaging DTOs — are invisible to that inference. ## What the annotation is `@RegisterReflectionForBinding` (`org.springframework.aot.hint.annotation`) is **meta-annotated** with `@Reflective(RegisterReflectionForBindingProcessor.class)`. So it *is* an application of the @Reflective mechanism — not a separate system. You put it on a `@Configuration` class, any bean, or a method, and list the target classes: ```java @Configuration @RegisterReflectionForBinding({ OrderDto.class, CustomerDto.class }) class SerializationHints {} ``` `value()` and `classes()` are aliases (`Class<?>[]`). ## What its processor does `RegisterReflectionForBindingProcessor` implements `ReflectiveProcessor`. In `registerReflectionHints` it reads the annotation's class list and, for each class, delegates to **`BindingReflectionHintsRegistrar`** (`org.springframework.aot.hint.BindingReflectionHintsRegistrar`). That registrar is the key difference from `SimpleReflectiveProcessor`: instead of registering a single member, it **recursively analyses the type for binding** — public and declared fields, property accessor methods, constructors (including record canonical constructors), superclass and generic/nested types, enum constants, and Kotlin/JavaBean conventions — and registers the member categories a serializer requires (e.g. invoke declared constructors, invoke declared methods, declared fields). ## When to use it - DTOs bound by Jackson for HTTP request/response bodies that Spring can't infer. - Message payloads (Kafka, RabbitMQ, WebSocket) deserialized reflectively. - `RestClient`/`WebClient`/`RestTemplate` body types resolved generically. - Any object graph you serialize to/from JSON/other formats in native. ## Gotchas & related - It's **not** for enabling reflective *business* calls — use `@RegisterReflection` (Spring 6.2) or a custom processor for that. `@RegisterReflection` also builds on `@Reflective` (via `RegisterReflectionReflectiveProcessor`) and lets you specify explicit `MemberCategory` values. - The annotated holder must be a **bean/reachable class**, because scanning is bean-scoped; annotating a random unused class does nothing. - Alternatives: implement `RuntimeHintsRegistrar` and register via `@ImportRuntimeHints`, or call `BindingReflectionHintsRegistrar` yourself. The annotation is just the ergonomic path. - Failures only surface when you run the **native binary**, so exercise serialization in native tests.

  • What's the difference between @RegisterReflectionForBinding and @RegisterReflection?
    Both are meta-annotated with @Reflective. @RegisterReflectionForBinding targets serialization: its processor uses BindingReflectionHintsRegistrar to walk the type graph and register everything a binder needs. @RegisterReflection (Spring 6.2) is general-purpose — you explicitly declare classes/classNames and the exact MemberCategory values you want.
  • You added @RegisterReflectionForBinding but binding still fails in native. What would you check?
    Confirm the annotated holder is actually a registered bean (scanning is bean-scoped), that the correct DTO classes (including nested/generic element types) are listed, that AOT processing ran (native build, not plain JVM), and reproduce via a native test since JVM tests won't catch it.

saying these in an interview costs you the question

  • Thinking it enables reflective invocation of arbitrary business methods rather than binding.
  • Assuming Spring always infers DTO hints so the annotation is never needed.
  • Believing it works at runtime on the JVM instead of at AOT/native build time.
  • Not realizing it's just a preconfigured use of the @Reflective mechanism.

context

open as a page

What is the @Reflective meta-annotation and what problem does it solve for GraalVM native images?

level: juniorimportance: should knowfreq 25%

basics

~20 s

GraalVM native image drops reflection metadata it can't see at build time. @Reflective is a Spring meta-annotation you put on another annotation so that, during AOT, Spring automatically registers reflection hints for every element marked with it.

open as a page

What is the ReflectiveProcessor interface, and what does the default SimpleReflectiveProcessor register?

level: middleimportance: should knowfreq 15%

basics

~10 s

ReflectiveProcessor is an interface with one method, registerReflectionHints(ReflectionHints, AnnotatedElement), that decides which hints to emit. SimpleReflectiveProcessor is the default: it registers the type, or the specific constructor/method for reflective invocation.

open as a page

How would you create a custom annotation that contributes your own reflection hints via a ReflectiveProcessor?

level: seniorimportance: nice to knowfreq 10%

basics

~10 s

Write a ReflectiveProcessor implementing registerReflectionHints, then create your own annotation meta-annotated with @Reflective(YourProcessor.class). Put your annotation on beans/members; AOT scanning invokes the processor and emits the hints.

open as a page

When and over what scope does @Reflective processing run, and what are the consequences of that design?

level: principalimportance: nice to knowfreq 8%

basics

~20 s

It runs at AOT build time, not at runtime. A BeanFactoryInitializationAotProcessor scans registered bean classes and their members for @Reflective-meta-annotated annotations. So only beans (and their reachable members) are covered; arbitrary classpath types are not.

open as a page