What subtle behaviors of bean() around bean naming, aliases, FactoryBeans, and infrastructure beans should you know?
answer
- matches registered name: @Bean method name / uncapitalized class
- FactoryBean: matches product, not &name factory
- infra/BeanPostProcessors not proxied -> unadvisable
- self-invocation still bypasses proxy
- rename = silent re-scope; verify getBeanDefinitionNames
basics
~20 sbean() matches the container's registered bean name. For a FactoryBean it matches the produced object's name (not the &name factory), it can't advise infrastructure/non-proxied beans, and it matches names not aliases in a predictable way — so know exactly what name a bean carries.
solid answer
~50 sbean() resolves against the bean name the container knows at proxy-creation time, which produces several subtleties. For a FactoryBean, matching applies to the exposed bean produced by the factory (retrieved as name), not to the &name FactoryBean object itself. Only beans that Spring actually proxies can be advised — many framework/infrastructure beans (BeanPostProcessors, the like) are created too early or excluded, so bean() can't advise them regardless of name. Because it's name-based, whatever name/id a bean is registered under is what you must match; a @Bean method name or explicit name attribute governs it, and default names come from the uncapitalized class name. bean() is evaluated by Spring's proxy AOP only. Practically: verify the actual bean name (getBeanDefinitionNames), remember same-class beans differ by name, and don't rely on bean() to advise container internals or self-invocations.
code
java · 15 lines@Configuration
class AppConfig {
// Bean NAME is the method name -> "pricingService", NOT "tradeService".
// bean(*Service) matches "pricingService"; bean(tradeService) would NOT.
@Bean
TradeService pricingService() { return new TradeService(); }
}
@Aspect @Component
class AuditAspect {
// Matches the exposed product of a FactoryBean under its plain name,
// never the &name factory object.
@Before("bean(pricingService)")
void audit() { /* ... */ }
}go deeper
Know the name comes from how the bean is registered.
Know @Bean name = method name and that only proxied beans get advice.
Explain FactoryBean product matching and infrastructure/self-invocation limits.
Anticipate name-source subtleties, non-proxied targets, and rename-driven silent re-scoping in reviews.
## What name is being matched? `bean()` matches the **registered bean name/id**. Know how that name is chosen: - **Component scanning:** default name = uncapitalized simple class name (`TradeService` -> `tradeService`), unless overridden via `@Service("...")` / `@Component("...")`. - **@Bean methods:** the **method name** is the bean name, unless `@Bean(name=...)` / `@Bean("...")` overrides it. - **XML:** the `id`/`name` attribute. Matching is only as good as your knowledge of these names — `bean(*Service)` silently misses a service registered under `@Bean("pricing")`. ## FactoryBean semantics When a bean is produced by a `FactoryBean`, the container exposes two things: the **product** under `name`, and the **factory itself** under `&name`. For `bean()`, matching targets the **exposed product** (the object you get from `getBean(name)`), consistent with how Spring resolves the name — not the `&name` FactoryBean object. This matters for things like Mybatis mappers or `ProxyFactoryBean`-style setups where a FactoryBean fronts the real bean. ## Only proxied beans can be advised `bean()` narrows a pointcut, but advice can only apply to beans Spring **actually wraps in a proxy**. Several categories are effectively unadvisable no matter the name: - **AOP infrastructure and BeanPostProcessors** — instantiated very early, before the auto-proxy machinery, so they aren't proxied. - **Beans excluded from auto-proxying** or created outside the container. So `bean(*Processor)` won't advise a `BeanPostProcessor` just because its name matches. ## Self-invocation still bites `bean()` doesn't change the fundamental proxy limitation: a bean calling its **own** method via `this.foo()` bypasses the proxy, so the advice — even if the bean name matches — does **not** run for internal calls. Only external calls that pass through the proxy are advised. ## Same class, distinct names Because matching is by name, two beans of the same class registered under different names are **independently** selectable — a capability unique to `bean()`. Conversely a rename silently re-scopes advice with no compiler signal, which is the main governance hazard. ## Practical verification When a `bean()` rule 'doesn't fire', check the **actual** registered name (`applicationContext.getBeanDefinitionNames()` / debug the `AspectJExpressionPointcut`), confirm the target is **proxied**, and confirm the call is **external**. Most 'bean() bug' reports are a name mismatch, an infrastructure/non-proxied target, or self-invocation — not a defect in the designator.
- A @Bean method named pricingService returns a TradeService. What does bean(tradeService) match?Nothing here — the bean name is the method name 'pricingService', not the class-derived 'tradeService'. bean() matches the registered name, so you'd need bean(pricingService) or bean(*Service).
- Why can't bean(*Processor) advise your BeanPostProcessor even though the name matches?BeanPostProcessors are instantiated very early, before the auto-proxy infrastructure exists, so they're never proxied. bean() can only narrow to beans Spring actually proxies; infrastructure beans are outside that set regardless of name.
- For a FactoryBean registered as 'sqlSession', does bean(sqlSession) target the factory or the product?The product (the object returned by getBean("sqlSession")), not the &sqlSession FactoryBean itself.
saying these in an interview costs you the question
- Thinking bean() matches the FactoryBean object rather than its product
- Assuming a @Bean's name is the class name rather than the method name
- Believing bean() can advise BeanPostProcessors/infrastructure beans
- Expecting bean() to catch self-invocation calls