What is the @Reflective meta-annotation and what problem does it solve for GraalVM native images?
answer
- closed-world native image drops reflection
- meta-annotation -> auto reflection hints
- value() = ReflectiveProcessor, default SimpleReflectiveProcessor
- scanned at AOT build time
- org.springframework.aot.hint.annotation
basics
~20 sGraalVM 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.
solid answer
~40 sA GraalVM native image is built with closed-world analysis: the compiler statically decides what code and metadata to keep, and reflective access that isn't declared up front fails at runtime. Spring bridges this with AOT-generated 'reflection hints'. @Reflective (in org.springframework.aot.hint.annotation) is a meta-annotation: when a custom or built-in annotation is itself annotated with @Reflective, Spring's AOT engine scans beans for elements (types, methods, fields, constructors) carrying that annotation and contributes the matching reflection hints automatically. @Reflective names one or more ReflectiveProcessor implementations that decide exactly which hints to emit; the default is SimpleReflectiveProcessor. This is the low-level mechanism behind higher-level conveniences like @RegisterReflectionForBinding. It removes the need to hand-write a RuntimeHintsRegistrar for annotation-driven cases.
code
java · 11 lines// A built-in Spring example: @RegisterReflectionForBinding is meta-annotated
// with @Reflective, which is what makes AOT register hints for it.
package org.springframework.aot.hint.annotation;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Reflective(RegisterReflectionForBindingProcessor.class) // <-- the mechanism
public @interface RegisterReflectionForBinding {
@AliasFor("classes") Class<?>[] value() default {};
@AliasFor("value") Class<?>[] classes() default {};
}go deeper
Know that native images need reflection declared up front and @Reflective automates that for annotated elements.
Explain that @Reflective points at a ReflectiveProcessor and is scanned during AOT over beans.
Connect it to RuntimeHints/ReflectionHints and name it as the engine behind @RegisterReflectionForBinding.
Discuss bean-scoped scanning, the BeanFactoryInitializationAotProcessor, and build-time vs runtime failure modes.
## Background: why hints exist When you compile a Spring app to a GraalVM **native image**, GraalVM performs **closed-world / ahead-of-time (AOT) analysis**: it statically walks all reachable code at build time and discards anything it can't prove is used. **Reflection** (looking up methods/fields/constructors by name at runtime via `java.lang.reflect`) is opaque to that analysis, so any reflective access must be *declared in advance*. GraalVM reads this from `reachability-metadata`/`reflect-config.json` files. Spring generates those files during its **AOT processing phase** by producing `RuntimeHints` — specifically `ReflectionHints`. ## What @Reflective is `@Reflective` is a **meta-annotation** in package `org.springframework.aot.hint.annotation`. You don't usually put it directly on a class; you put it on *another annotation*. Any annotation meta-annotated with `@Reflective` becomes a signal: 'whenever Spring's AOT engine finds an element (a type, constructor, method, or field of a bean) annotated with me, register reflection hints for that element.' Its single attribute is: ```java Class<? extends ReflectiveProcessor>[] value() default SimpleReflectiveProcessor.class; ``` So `@Reflective` points at one or more **`ReflectiveProcessor`** implementations that do the actual hint registration. If you don't name one, `SimpleReflectiveProcessor` is used. ## How scanning happens At build time a `BeanFactoryInitializationAotProcessor` — `org.springframework.context.aot.ReflectiveProcessorBeanFactoryInitializationAotProcessor` — iterates the registered bean classes and uses `ReflectiveRuntimeHintsRegistrar` to look for `@Reflective`-meta-annotated annotations on each type and its members. For every match it invokes the referenced `ReflectiveProcessor`, which writes entries into `ReflectionHints`. Those become the generated reflection metadata. ## Why it matters Without hints, a JVM app runs fine (JIT, full reflection), but the native image throws `ClassNotFoundException`, `NoSuchMethodException`, or silently missing beans/serialization. `@Reflective` gives library and framework authors a declarative, annotation-driven way to attach reflection hints instead of writing an imperative `RuntimeHintsRegistrar`. ## Gotchas - Hints only affect **native image**; on the JVM they're inert. - Scanning is **bean-scoped** — the element must belong to a class Spring knows about as a bean (or reachable member), not any arbitrary classpath type. - It runs at **build time**; a wrong or missing hint only shows up when you actually run the native binary.
- Do these hints do anything when running on a normal JVM?No. On the JVM reflection is unrestricted, so the hints are ignored. They only matter when producing a GraalVM native image, where reflection metadata must be declared at build time.
saying these in an interview costs you the question
- Thinking @Reflective affects runtime behaviour on the plain JVM.
- Believing it scans the entire classpath rather than Spring beans.
- Confusing reflection hints with dependency injection annotations.