What is the ReflectiveProcessor interface, and what does the default SimpleReflectiveProcessor register?
answer
- one method: registerReflectionHints(hints, element)
- AnnotatedElement = Class/Method/Constructor/Field
- SimpleReflectiveProcessor branches on element type
- executables registered with ExecutableMode.INVOKE
- narrow by design — only the annotated member
basics
~10 sReflectiveProcessor 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.
solid answer
~40 sReflectiveProcessor (org.springframework.aot.hint.annotation) is the strategy that a @Reflective-annotated annotation binds to. Its single method, registerReflectionHints(ReflectionHints hints, AnnotatedElement element), receives the annotated element — a Class, Constructor, Method, or Field — and writes entries into ReflectionHints. The default, SimpleReflectiveProcessor, branches on the element type: for a Class it registers the type itself; for a Constructor or Method it registers it with ExecutableMode.INVOKE so it can be called reflectively; for a Field it registers the field. It deliberately registers narrowly — just enough to reflect on the exact annotated member — rather than opening up the whole type. If you need broader access (all fields, all constructors) or to walk a type graph, you supply a custom ReflectiveProcessor via @Reflective's value attribute.
code
java · 16 lines// Simplified view of the default processor's dispatch logic
public class SimpleReflectiveProcessor implements ReflectiveProcessor {
@Override
public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) {
if (element instanceof Class<?> type) {
registerTypeHint(hints, type); // hints.registerType(type)
} else if (element instanceof Constructor<?> ctor) {
registerConstructorHint(hints, ctor); // INVOKE
} else if (element instanceof Field field) {
registerFieldHint(hints, field);
} else if (element instanceof Method method) {
registerMethodHint(hints, method); // INVOKE
}
}
// protected methods are overridable to broaden the hints
}go deeper
Know the default processor exists and emits hints for the annotated element.
Describe the single-method interface and SimpleReflectiveProcessor's per-element-type behaviour.
Explain ExecutableMode.INVOKE vs INTROSPECT and why the default is deliberately narrow.
Reason about metadata size trade-offs and when to override protected hooks vs write a fresh processor.
## The interface ```java package org.springframework.aot.hint.annotation; @FunctionalInterface public interface ReflectiveProcessor { void registerReflectionHints(ReflectionHints hints, AnnotatedElement element); } ``` - **`ReflectionHints`** (`org.springframework.aot.hint`) is the mutable collection of reflection metadata being built during AOT. You call methods like `registerType(...)`, `registerMethod(...)`, `registerConstructor(...)`, `registerField(...)` on it. - **`AnnotatedElement`** is the JDK reflection abstraction implemented by `Class`, `Method`, `Constructor`, and `Field` — i.e. whatever element carried the `@Reflective`-meta-annotated annotation. A `ReflectiveProcessor` is therefore a **strategy**: given the element that triggered scanning, decide precisely which hints to emit. ## SimpleReflectiveProcessor — the default When `@Reflective` names no processor, `SimpleReflectiveProcessor` is used. Its logic: - **`Class`** → `hints.registerType(type)` (registers the type for reflection; by default this makes the type known/introspectable without necessarily opening all members). - **`Constructor`** → `hints.registerConstructor(constructor, ExecutableMode.INVOKE)` so it can be **invoked** reflectively (e.g. `newInstance`). - **`Method`** → `hints.registerMethod(method, ExecutableMode.INVOKE)` so it can be invoked reflectively. - **`Field`** → `hints.registerField(field)`. **`ExecutableMode`** has two values: `INTROSPECT` (you can look the executable up / read its metadata) and `INVOKE` (you can also call it). `SimpleReflectiveProcessor` uses `INVOKE` for executables so the annotated method/constructor is actually callable. Key point: it is **surgical** — it registers only the exact annotated member, not the entire declaring type's members. That keeps the generated metadata (and native image size) minimal. ## When to subclass vs replace `SimpleReflectiveProcessor`'s per-element methods (`registerTypeHint`, `registerMethodHint`, etc.) are `protected`, so you can extend it and override just one branch — e.g. register a whole class's declared fields when the annotation lands on a type. Or implement `ReflectiveProcessor` from scratch for arbitrary logic. ## Gotchas - The method is called **once per matching element**, at build time. - Registering a type alone does not automatically register its members for invocation — for serialization you need member categories (that's what `BindingReflectionHintsRegistrar` / `@RegisterReflectionForBinding` add). - Because it's a `@FunctionalInterface`, a processor can be a lambda, but real ones are usually classes so `@Reflective(MyProcessor.class)` can name them.
- What is the difference between ExecutableMode.INTROSPECT and ExecutableMode.INVOKE?INTROSPECT lets you discover/look up the constructor or method and read its metadata; INVOKE additionally allows actually calling it reflectively. SimpleReflectiveProcessor uses INVOKE for annotated executables.
saying these in an interview costs you the question
- Claiming SimpleReflectiveProcessor opens up all members of the declaring class.
- Saying ReflectiveProcessor runs at application startup rather than AOT build time.
- Confusing registering a type with registering its methods for invocation.