skip to content

How do you use @RegisterReflectionForBinding, and what do its 'classes' and 'classNames' attributes do?

level: middleimportance: should knowfreq 28%

answer

  1. classes = Class<?> refs (type-safe)
  2. classNames = String FQNs (no hard dep / package-private)
  3. @Target TYPE + METHOD, @Repeatable
  4. Walks the property graph, not just the one class
  5. classNames typo fails silently at native runtime

basics

~20 s

Put @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 s

You 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 lines
java
import 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

for a junior

Should know you list DTO classes on a config/bean to keep them for native serialization.

for a middle

Should distinguish classes vs classNames and note @Repeatable and TYPE/METHOD targets.

for a senior

Should note the property-graph traversal and the silent-failure risk of classNames typos.

for a principal

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

context