Explain the key BeanDefinition flags — scope, lazy-init, primary, and autowire-mode — and their exact semantics and edge cases.
answer
- singleton per-container, prototype no destroy callback
- lazy = created on first use, eager dependent forces it
- primary = preferred candidate, two primaries = error
- @Qualifier beats @Primary
- autowireMode (XML) ≠ @Autowired
basics
~10 sScope controls instance lifecycle (singleton vs prototype/web). Lazy-init delays singleton creation until first use. Primary marks the preferred autowire candidate among several. Autowire-mode is the legacy XML setting (byName/byType/constructor) for automatic dependency resolution.
solid answer
~40 s**Scope**: `singleton` (one shared instance per container, default) or `prototype` (new instance per lookup — Spring doesn't manage its full lifecycle or call destroy), plus web scopes (request/session). **Lazy-init**: a `true` flag makes a singleton created on first access instead of eagerly at refresh; note an eager non-lazy bean that depends on a lazy one still forces its creation. **Primary**: when multiple candidates match a type, the `primary=true` one wins autowiring; two primaries for one type cause `NoUniqueBeanDefinitionException`. **Autowire-mode** (`AUTOWIRE_NO/BY_NAME/BY_TYPE/CONSTRUCTOR`) is a legacy XML/programmatic mode that auto-resolves *setter/constructor* dependencies without explicit refs — it is **distinct from and mostly superseded by** `@Autowired`, which is annotation-driven and always active regardless of this flag.
code
java · 25 lines@Configuration
class AppConfig {
@Bean
@Scope("prototype") // new instance each request; no destroy callback
Parser parser() { return new Parser(); }
@Bean
@Lazy // created on first access, not at refresh
ExpensiveCache cache() { return new ExpensiveCache(); }
@Bean
@Primary // preferred when a PaymentGateway is autowired
PaymentGateway stripeGateway() { return new StripeGateway(); }
@Bean
PaymentGateway paypalGateway() { return new PaypalGateway(); }
}
@Service
class Checkout {
// Resolves to stripeGateway (@Primary) without a @Qualifier here.
Checkout(PaymentGateway gateway) { }
// Adding @Qualifier("paypalGateway") on the param would override @Primary.
}go deeper
Know the four flags at a high level: scope=lifecycle, lazy=defer, primary=preferred, autowire=auto-wiring.
Give exact defaults and that prototype lacks destroy callbacks; explain primary vs qualifier.
Cover lazy edge cases (eager dependent forces creation, injection-point @Lazy proxy) and autowire-mode ≠ @Autowired.
Discuss scoped proxies for prototype-in-singleton, @Fallback inverse of primary, custom Scope SPI, and startup-error timing trade-offs of lazy.
## scope The `scope` string controls instance management: - **`singleton`** (default): exactly **one** instance per container, cached and shared, full lifecycle (init + destroy callbacks). - **`prototype`**: a **new** instance every time the bean is requested/injected. Spring instantiates and wires it but then **hands off** — it does **not** track it or call destroy callbacks (you must clean up yourself). Injecting a prototype into a singleton captures a single instance unless you use a lookup mechanism (`@Lookup`, `ObjectProvider`, or scoped proxy). - **Web scopes**: `request`, `session`, `application`, `websocket` — need a web-aware context; typically backed by a scoped proxy so a singleton can hold a reference. - Custom scopes via `Scope` SPI + `registerScope`. ## lazy-init `lazyInit=true` (from `@Lazy` or XML `lazy-init`) means a **singleton** is not pre-instantiated at the end of `refresh()`; it's created on **first access**. Edge cases: - A non-lazy singleton that **depends on** a lazy bean will trigger the lazy bean's creation at startup anyway — laziness is only honored if nothing eagerly needs it. - `@Lazy` on an **injection point** injects a lazy proxy, breaking eager creation and helping with circular dependencies. - Lazy has no meaning for prototypes (they're always created on demand). - Downside: startup errors in a lazy bean surface later, at first use, not at boot. ## primary `primary=true` (`@Primary` or XML `primary`) designates the **preferred candidate** when several beans match a required type during autowiring. Semantics: - Resolves the common 'multiple candidates' ambiguity **without** the injection site needing `@Qualifier`. - **Exactly one** primary allowed per type at a resolution point; two primaries → `NoUniqueBeanDefinitionException`. - `@Qualifier` at the injection point **overrides** primary (explicit name wins). - Newer alternative: `@Fallback` (Spring 6.2) marks a bean as lower-priority, the inverse of primary. ## autowire-mode (legacy) The `autowireMode` int (`AbstractBeanDefinition.AUTOWIRE_NO`, `AUTOWIRE_BY_NAME`, `AUTOWIRE_BY_TYPE`, `AUTOWIRE_CONSTRUCTOR`) is the **XML-era/programmatic** automatic wiring mode: - **BY_NAME**: match setter properties to beans with the same name. - **BY_TYPE**: match setter properties to a unique bean of the property's type. - **CONSTRUCTOR**: like by-type but for constructor args. - **NO** (default): no automatic mode; you wire explicitly or via annotations. Critically, this is **not** the same as `@Autowired`. `@Autowired` is processed by `AutowiredAnnotationBeanPostProcessor` and works **regardless** of the definition's autowire-mode. The autowire-mode flag applies to properties/constructor args **not otherwise specified**, only really used with XML or `BeanDefinitionBuilder.setAutowireMode`. Modern annotation-config apps leave it at NO. ## Common gotchas - 'Singleton' is **per Spring container**, not the GoF JVM-wide singleton pattern. - Prototype beans don't get destroy callbacks — resource leaks if you assume they do. - Setting `primary` on two beans of the same type is a frequent cause of startup failure. - Confusing `autowireMode=BY_TYPE` with `@Autowired` — they are independent mechanisms. ## When to set which - Scope: prototype for stateful/per-use objects; web scopes for request/session state. - Lazy: expensive rarely-used beans, or to break init-order/circular issues (prefer at injection point). - Primary: sensible default implementation among many. - Autowire-mode: essentially only legacy XML — prefer annotations/constructor injection.
- If a non-lazy singleton depends on a @Lazy bean, is the lazy bean still created at startup?Yes, unless the injection point itself uses @Lazy (injecting a proxy). Definition-level lazy is only honored when nothing eagerly needs the bean; a direct eager dependency forces its creation at refresh.
- How do @Primary and @Qualifier interact when both could apply?@Qualifier at the injection point wins — it names an exact bean, overriding @Primary. @Primary is only the tiebreaker when no qualifier narrows the candidates.
- Does setting autowireMode to BY_TYPE affect @Autowired fields?No. @Autowired is handled independently by AutowiredAnnotationBeanPostProcessor and works regardless of the definition's autowire-mode. The autowire-mode flag is a separate legacy XML mechanism for unspecified setter/constructor args.
saying these in an interview costs you the question
- Saying prototype beans get destroy callbacks (they don't — Spring stops managing them after creation).
- Treating singleton as a JVM-wide GoF singleton rather than one-per-container.
- Claiming @Autowired requires setting autowire-mode on the definition.
- Believing you can mark two beans of the same type @Primary without error.