What is an instance supplier in AOT-generated bean definitions, and how does it replace reflective bean instantiation?
answer
- setInstanceSupplier replaces newInstance()
- BeanInstanceSupplier.forConstructor(...).withGenerator(...)
- forFactoryMethod(configClass, name, argTypes)
- args = AutowiredArguments resolved by Spring
- direct typed call, not reflective
basics
~20 sAn instance supplier is a callback set on the BeanDefinition (via setInstanceSupplier) that creates the bean by directly calling its constructor or factory method in generated code, instead of Spring reflectively finding and invoking the constructor at runtime.
solid answer
~40 sNormally Spring instantiates a bean by reflectively selecting a constructor, resolving its argument beans, and calling `Constructor.newInstance(...)`. In AOT mode the generated `*__BeanDefinitions` code instead calls `beanDefinition.setInstanceSupplier(...)` with a `BeanInstanceSupplier`. Spring provides `BeanInstanceSupplier.forConstructor(paramTypes...)` and `forFactoryMethod(declaringClass, methodName, paramTypes...)`, each combined with `.withGenerator((registeredBean, args) -> new MyService(args.get(0)))`. The `forConstructor`/`forFactoryMethod` part still describes which member is used (so autowiring metadata and RuntimeHints are known), but the `withGenerator` lambda does the **direct** call — no reflective `newInstance`. Argument resolution (`args`) is still handled by Spring resolving dependent beans, but the final construction is a plain typed call. This is what makes instantiation reflection-free under GraalVM's closed world.
code
java · 23 lines// Constructor injection — generated instance supplier (simplified):
public static BeanInstanceSupplier<MyService> myServiceSupplier() {
return BeanInstanceSupplier
.<MyService>forConstructor(MyRepository.class) // WHICH member
.withGenerator((registeredBean, args) -> // HOW to build
new MyService(args.get(0))); // direct call, no reflection
}
public static BeanDefinition getMyServiceBeanDefinition() {
RootBeanDefinition bd = new RootBeanDefinition(MyService.class);
bd.setInstanceSupplier(myServiceSupplier());
return bd;
}
// @Bean factory method — the generator calls the method on the config bean:
public static BeanInstanceSupplier<MyService> fromFactory() {
return BeanInstanceSupplier
.<MyService>forFactoryMethod(MyConfig.class, "myService", MyRepository.class)
.withGenerator((registeredBean, args) ->
registeredBean.getBeanFactory()
.getBean(MyConfig.class)
.myService(args.get(0)));
}go deeper
Know that an instance supplier is a callback that creates the bean directly instead of via reflection.
Explain setInstanceSupplier, the forConstructor/forFactoryMethod + withGenerator structure, and how @Bean methods are called directly.
Discuss why member metadata is kept separate from the generator (argument resolution + hints) and reflective fallbacks.
Reason about accessibility/visibility constraints on generated code, fallback-to-reflection cases, and the boundary between construction and post-construction injection reflection.
## Reflective instantiation (the default runtime path) Without AOT, when Spring needs a bean it uses `ConstructorResolver`/`SimpleInstantiationStrategy` to: (1) pick the constructor (annotated `@Autowired` or the single available one), (2) reflectively resolve each parameter to a bean, (3) call `Constructor.newInstance(...)`. All three steps rely on reflection over class metadata that must exist at runtime. ## The instance supplier `org.springframework.beans.factory.support.RootBeanDefinition` has `setInstanceSupplier(Supplier<?>)`. When present, the container uses that supplier to obtain the instance instead of the reflective strategy. AOT generation exploits this: for every bean, the generated code sets an instance supplier that constructs the object with a **direct, typed** call. Spring supplies a specialized `Supplier` for this: `org.springframework.beans.factory.aot.BeanInstanceSupplier<T>`. You build it in two parts: - **What member to call** — `BeanInstanceSupplier.forConstructor(Class<?>... parameterTypes)` or `BeanInstanceSupplier.forFactoryMethod(Class<?> declaringClass, String methodName, Class<?>... parameterTypes)`. This retains the *description* of the member so Spring can resolve arguments and register the right reflection hints. - **How to build it** — `.withGenerator((RegisteredBean registeredBean, AutowiredArguments args) -> new MyService(args.get(0)))`. This lambda performs the actual construction with a compile-time-typed call. There's also a `ThrowingBiFunction` overload and a `withGenerator(registeredBean -> ...)` form for no-arg cases. ## Argument resolution still happens — but construction doesn't reflect The `args` (`AutowiredArguments`) passed to the generator are resolved by Spring from the `BeanFactory` (the constructor's dependencies are still real beans). What changed is the **final call**: `new MyService(args.get(0))` is a direct bytecode `invokespecial`, not `Constructor.newInstance`. For a `@Bean` factory method, the generator obtains the config bean and calls the method directly: `registeredBean.getBeanFactory().getBean(MyConfig.class).myService(args.get(0))`. ## Why the two-part design Keeping `forConstructor(...)` metadata separate from the generator lets Spring: - Register the correct `RuntimeHints`/reflection metadata for the member (needed when something still inspects it). - Resolve autowired arguments generically, including handling of `@Qualifier`, `Optional`, `ObjectProvider`, etc. - Support post-processing that adjusts arguments. ## Gotchas - The generator lambda references the real types, so **visibility matters**: package-private constructors force the generated class into the same package (Spring handles this by placing generated code in the target's package). - If a constructor argument can't be expressed in generated code (e.g., unusual generic or inaccessible type), AOT may fall back to a reflective supplier and register a reflection hint instead. - Instance suppliers replace only **instantiation**. Field/method injection performed after construction may still use reflection, for which AOT registers hints. - Don't confuse `setInstanceSupplier` with `@Bean` `Supplier` beans or with `ObjectProvider` — it's an internal instantiation hook, not application API you'd normally call.
- If instance suppliers avoid reflection, why does AOT still register reflection hints for some beans?Instance suppliers only cover construction. Field/setter injection, JPA entity access, Jackson (de)serialization, validation, and any framework that introspects the bean still reflect, so AOT emits `RuntimeHints` so `native-image` keeps that metadata.
- What role do `forConstructor(...)` / `forFactoryMethod(...)` play if the generator lambda already builds the object?They describe the member so Spring can resolve autowired arguments (`AutowiredArguments`), apply qualifiers/optionality, and register the correct reflection hints. The lambda only performs the final typed construction call.
saying these in an interview costs you the question
- Saying the instance supplier still calls Constructor.newInstance internally
- Claiming instance suppliers remove ALL reflection including injection and serialization
- Thinking `setInstanceSupplier` is an application-level API you normally call in a @Bean method
- Confusing BeanInstanceSupplier with an ObjectProvider or a @Bean of type Supplier<T>