More Bean Ambiguity Strategies, Custom Scopes, and BeanPostProcessor
Additional Bean Ambiguity Resolution Strategies
Section titled “Additional Bean Ambiguity Resolution Strategies”Beyond @Qualifier and @Primary, a few other approaches resolve NoUniqueBeanDefinitionException:
Injecting by Bean Name
Section titled “Injecting by Bean Name”@AutowiredPaymentService paypalPaymentService;Spring matches the field name with the bean name.
Using @Resource
Section titled “Using @Resource”@Resource(name = "paypalPaymentService")PaymentService paymentService;Injecting All Implementations as a Collection
Section titled “Injecting All Implementations as a Collection”@AutowiredList<PaymentService> paymentServices;or
@AutowiredMap<String, PaymentService> paymentServices;This approach is commonly used to implement the Strategy pattern — the caller picks the right implementation from the collection at runtime instead of Spring picking one at injection time.
Creating a Custom Bean Scope
Section titled “Creating a Custom Bean Scope”Step 1: Implement the Scope Interface
Section titled “Step 1: Implement the Scope Interface”public class CustomScope implements Scope {
private Map<String, Object> beans = new HashMap<>();
@Override public Object get(String name, ObjectFactory<?> objectFactory) { return beans.computeIfAbsent(name, k -> objectFactory.getObject()); }
@Override public Object remove(String name) { return beans.remove(name); }}Step 2: Register the Scope
Section titled “Step 2: Register the Scope”@Beanpublic static CustomScopeConfigurer customScopeConfigurer() { CustomScopeConfigurer configurer = new CustomScopeConfigurer(); configurer.addScope("custom", new CustomScope()); return configurer;}Step 3: Use the Scope
Section titled “Step 3: Use the Scope”@Scope("custom")@Componentclass CustomScopedBean {}Real-World Singleton vs Prototype Use Cases
Section titled “Real-World Singleton vs Prototype Use Cases”| Singleton | Prototype |
|---|---|
| UserService | ReportBuilder |
| OrderService | File Processing Task |
| PaymentService | Data Transformation Object |
| Repository | Temporary Workflow Object |
Singletons are ideal for stateless shared services; prototype beans suit stateful or temporary objects.
BeanPostProcessor
Section titled “BeanPostProcessor”BeanPostProcessor allows custom processing before and after bean initialization.
Lifecycle
Section titled “Lifecycle”flowchart LR A[Instantiate Bean] --> B[Dependency Injection] B --> C[postProcessBeforeInitialization] C --> D[Initialization] D --> E[postProcessAfterInitialization] E --> F[Ready for Use]
Interface
Section titled “Interface”public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName);
Object postProcessAfterInitialization(Object bean, String beanName);}Example
Section titled “Example”@Componentpublic class CustomBeanPostProcessor implements BeanPostProcessor {
@Override public Object postProcessBeforeInitialization(Object bean, String beanName) { System.out.println("Before Init: " + beanName); return bean; }
@Override public Object postProcessAfterInitialization(Object bean, String beanName) { System.out.println("After Init: " + beanName); return bean; }}Common Uses
Section titled “Common Uses”- Logging
- Proxy creation
- Annotation processing
- Performance monitoring
- Security
Spring internally uses BeanPostProcessor to implement:
@Autowired@Transactional@Async@Cacheable
Key Takeaways
Section titled “Key Takeaways”@Resourceand bean-name matching are lighter-weight alternatives to@Qualifier; injecting aList/Mapof all implementations is the idiomatic way to wire up a Strategy pattern.- A custom bean scope requires implementing
Scope, registering it viaCustomScopeConfigurer, then referencing it by name in@Scope("custom")— rarely needed, but explains how the built-in scopes work under the hood. BeanPostProcessoris the extension point Spring itself uses to implement@Autowired,@Transactional,@Async, and@Cacheable— it’s the mechanism, not just an app-level customization hook.