skip to content

Explain the key BeanDefinition flags — scope, lazy-init, primary, and autowire-mode — and their exact semantics and edge cases.

level: seniorimportance: must knowfreq 50%

answer

  1. singleton per-container, prototype no destroy callback
  2. lazy = created on first use, eager dependent forces it
  3. primary = preferred candidate, two primaries = error
  4. @Qualifier beats @Primary
  5. autowireMode (XML) ≠ @Autowired

basics

~10 s

Scope 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
java
@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

for a junior

Know the four flags at a high level: scope=lifecycle, lazy=defer, primary=preferred, autowire=auto-wiring.

for a middle

Give exact defaults and that prototype lacks destroy callbacks; explain primary vs qualifier.

for a senior

Cover lazy edge cases (eager dependent forces creation, injection-point @Lazy proxy) and autowire-mode ≠ @Autowired.

for a principal

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.

context