How do you register beans programmatically with GenericApplicationContext and registerBean, and when would you do that?
answer
- Generic = register then refresh once
- registerBean(Class, Supplier, customizers...)
- Supplier = no reflection (AOT/native friendly)
- BeanDefinitionCustomizer sets scope/primary/lazy
- second refresh → IllegalStateException
basics
~10 sCreate a GenericApplicationContext, call registerBean(...) passing a class (and optionally a supplier/lambda and customizers) for each bean, then call refresh() once. This is the functional bean-registration style — useful for lightweight, reflection-free wiring.
solid answer
~40 sGenericApplicationContext lets you register bean definitions imperatively instead of via component scanning. The functional overloads — registerBean(Class), registerBean(name, Class, Supplier, BeanDefinitionCustomizer...) — take an optional instance Supplier (a lambda that constructs the bean) and customizers to tweak the definition (scope, primary, lazy, autowire). Because you supply the instance yourself, Spring skips reflective constructor resolution, which makes startup faster and works well with GraalVM native images / the functional bean style. You must call refresh() exactly once after registering; GenericApplicationContext (and AnnotationConfigApplicationContext, which extends it) guards against a second refresh. Use it for programmatic bootstrap, embedding Spring, tests, or Kotlin/Java functional configuration where you want explicit control instead of annotation scanning.
code
java · 11 linesvar ctx = new GenericApplicationContext();
// functional registration: supplier builds the instance, customizer marks it primary
ctx.registerBean("clock", Clock.class, Clock::systemUTC, bd -> bd.setPrimary(true));
ctx.registerBean(GreetingService.class,
() -> new GreetingService(ctx.getBean(Clock.class)));
ctx.refresh(); // exactly once — a second call throws IllegalStateException
GreetingService svc = ctx.getBean(GreetingService.class);
ctx.close();go deeper
Know that you can create beans in code with registerBean and must call refresh() afterward.
Explain the registerBean overloads with Supplier and customizers and the register-then-refresh-once flow.
Articulate the reflection-free/AOT motivation, the single-refresh guard, and how AnnotationConfigApplicationContext extends this.
Discuss when functional registration beats annotation scanning (native images, computed bean graphs, embedding) and the DSLs built on top of registerBean.
**GenericApplicationContext** is the general-purpose, single-use context. Unlike `ClassPathXmlApplicationContext` it does not tie you to one definition format — you populate it however you like and then call `refresh()`. **Ways to populate it:** - **Definition readers/scanners:** `new AnnotatedBeanDefinitionReader(ctx).register(Config.class)`, `new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(...)`, or `new ClassPathBeanDefinitionScanner(ctx).scan("com.acme")`. - **Functional `registerBean` (Spring 5+):** overloads on `GenericApplicationContext`: - `registerBean(Class<T> beanClass, Object... constructorArgs)` - `registerBean(Class<T> beanClass, Supplier<T> supplier, BeanDefinitionCustomizer... customizers)` - `registerBean(String beanName, Class<T> beanClass, Supplier<T> supplier, BeanDefinitionCustomizer... customizers)` The **`Supplier`** is an instance factory: when the container needs the bean it calls your lambda instead of reflecting over a constructor. That means *no reflective instantiation*, which is the basis of the 'functional bean registration' style and helps AOT/GraalVM native image builds. **`BeanDefinitionCustomizer`** is a functional interface receiving the `BeanDefinition` so you can set scope, `setPrimary(true)`, `setLazyInit(true)`, autowire mode, etc. **Refresh discipline.** After all `registerBean`/reader calls you invoke `ctx.refresh()` exactly once. `GenericApplicationContext` holds an internal `AtomicBoolean refreshed`; a second `refresh()` throws `IllegalStateException` ('GenericApplicationContext does not support multiple refresh attempts'). Registration must happen *before* refresh — once refresh has pre-instantiated singletons the definitions are effectively frozen (you can still `registerBean` after refresh in some cases, but the normal pattern is register-then-refresh). **AnnotationConfigApplicationContext relationship.** It extends `GenericApplicationContext` and simply pre-wires an `AnnotatedBeanDefinitionReader` + `ClassPathBeanDefinitionScanner`; its no-arg constructor lets you `register(...)`/`scan(...)` then `refresh()` manually, while the config-class constructor does register+refresh for you. So `registerBean` is available on it too. **When to use programmatic/functional registration:** - Reflection-free / AOT-friendly wiring (native images). - Conditional or computed bean sets decided at bootstrap. - Embedding Spring in a launcher, or building minimal test contexts. - Kotlin `beans { }` DSL (which is sugar over `registerBean`). **Gotchas:** (a) With a `Supplier`, autowiring of the bean's own constructor args won't happen — you construct it, so you fetch collaborators yourself or use other registration forms. (b) Forgetting `refresh()` on a bare `GenericApplicationContext` leaves an empty/unusable context. (c) The single-refresh limit means you can't 'reload' a GenericApplicationContext — for reloadable configs you'd need an `AbstractRefreshableApplicationContext` subclass.
- What happens if you call refresh() twice on a GenericApplicationContext?It throws IllegalStateException — GenericApplicationContext supports only a single refresh (guarded by an internal AtomicBoolean). Use an AbstractRefreshableApplicationContext subclass if you need reloadable definitions.
- What is the benefit of passing a Supplier to registerBean?Spring uses your lambda to create the instance instead of reflecting over a constructor. That avoids reflective instantiation, speeds startup, and is friendly to AOT/GraalVM native-image processing and the functional/Kotlin bean DSL.
saying these in an interview costs you the question
- Claiming GenericApplicationContext can be refreshed repeatedly to reload beans
- Forgetting the mandatory single refresh() call
- Believing a Supplier-registered bean still gets constructor autowiring
- Confusing registerBean (functional, since Spring 5) with XML <bean> registration