How do you use @RegisterReflectionForBinding, and what do its 'classes' and 'classNames' attributes do?
answer
- classes = Class<?> refs (type-safe)
- classNames = String FQNs (no hard dep / package-private)
- @Target TYPE + METHOD, @Repeatable
- Walks the property graph, not just the one class
- classNames typo fails silently at native runtime
basics
~20 sPut @RegisterReflectionForBinding on a bean or config class and list the DTO types to keep for serialization. Use classes for types available at build time; use classNames (string names) for types you can't reference directly, e.g. package-private or not on the config's classpath.
solid answer
~40 sYou annotate any Spring-managed component, a @Configuration class, or a method with @RegisterReflectionForBinding and pass the binding types. The classes attribute takes Class<?> references — the normal, type-safe way: @RegisterReflectionForBinding({OrderDto.class, LineItemDto.class}). The classNames attribute takes fully-qualified type names as Strings, for cases where you can't reference the Class directly at configuration time (package-private types, or types you don't want a hard compile dependency on). The annotation is @Repeatable and targets TYPE and METHOD. During AOT processing Spring registers the full set of reflection hints needed to serialize/deserialize each listed type, walking its property graph. On the JVM it does nothing; it only shapes the native-image reachability metadata.
code
java · 20 linesimport org.springframework.aot.hint.annotation.RegisterReflectionForBinding;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
@Configuration
// Type-safe references for types visible here...
@RegisterReflectionForBinding({OrderDto.class, LineItemDto.class})
// ...and a String name for a type we don't want a hard dependency on:
@RegisterReflectionForBinding(classNames = "com.acme.internal.LegacyOrderDto")
public class HttpClientConfig {
// OrderDto is deserialized reflectively from the response body;
// Spring can't infer this from a RestClient call, so we register it.
OrderDto fetchOrder(RestClient client, long id) {
return client.get()
.uri("/orders/{id}", id)
.retrieve()
.body(OrderDto.class);
}
}go deeper
Should know you list DTO classes on a config/bean to keep them for native serialization.
Should distinguish classes vs classNames and note @Repeatable and TYPE/METHOD targets.
Should note the property-graph traversal and the silent-failure risk of classNames typos.
Should reason about over-registration bloat vs. completeness and where in AOT the scan happens.
**Placement.** `@RegisterReflectionForBinding` has `@Target({ElementType.TYPE, ElementType.METHOD})` and is `@Repeatable`. In practice you put it on: a `@Configuration` class, any `@Component`/bean, or a specific method (handy to co-locate it with the code that actually does the binding). Spring's AOT engine scans these annotations during build-time processing and turns them into runtime hints. **`classes` attribute (the default).** Takes `Class<?>[]` — type-safe references resolved by the compiler: ```java @RegisterReflectionForBinding({OrderDto.class, LineItemDto.class}) ``` This is the preferred form whenever the types are visible at configuration time. **`classNames` attribute.** Takes `String[]` of fully-qualified class names: ```java @RegisterReflectionForBinding(classNames = "com.acme.internal.SecretDto") ``` Use it when you *cannot* or *do not want to* reference the `Class` object directly — e.g. the type is package-private and lives in another package, it's an internal type you don't want to create a hard compile-time dependency on, or it's only known by name. Semantically it registers the same binding hints; it's just a different way to name the target. **What gets registered.** Because this is the *binding* specialization, Spring doesn't just register the one class — it registers the reflection needed to serialize/deserialize it *and traverses the reachable property graph* (nested types, generic type arguments, accessor-visible types). Under the hood it delegates to `BindingReflectionHintsRegistrar`, which understands Jackson-style property discovery. **Repeatability and multiple sources.** Since it's `@Repeatable`, you can stack several annotations, and different beans across the app can each contribute their own binding registrations; Spring merges them all into the final metadata. **Gotchas.** (1) Only include types that are genuinely bound — over-registering bloats the image and weakens dead-code elimination. (2) `classNames` gives up compile-time safety: a typo or a renamed/moved class won't fail the build, it just silently won't register, surfacing as a native-runtime binding failure. (3) The annotation must be on something Spring's AOT processing actually visits (a scanned/registered bean or config), not a random unmanaged class. (4) Rebuild the native image after changing it.
- When would you prefer classNames over classes?When you can't reference the Class directly at config time — a package-private type in another package, or a type you don't want a hard compile-time dependency on. The trade-off is losing compile-time safety: a wrong name won't fail the build, it just won't register.
saying these in an interview costs you the question
- Claiming you must annotate the DTO class itself (it goes on a bean/config/method that Spring's AOT visits)
- Thinking classNames is somehow safer than classes
- Assuming registering a type ignores its nested/generic property types