02 - Dependency Injection and Beans Notes
A beginner-to-advanced guide to dependency injection, bean scopes, bean lifecycle, autowiring, qualifiers, and Spring container internals for Spring Professional Certification candidates. Covers Spring Framework 6 and Spring Boot 3 concepts.
Table of Contents
- Big Picture
- Dependency Injection
- Spring Bean Basics
- Spring Container
- Constructor Injection
- Setter Injection
- Field Injection
- Injection Type Comparison
- Autowiring Resolution
- Multiple Bean Candidates
- Qualifiers
@Primary- Bean Scopes
- Singleton Scope
- Prototype Scope
- Singleton Bean Depending on Prototype Bean
- Web Scopes
- Bean Lifecycle
- Lifecycle Diagram
- Initialization Callbacks
- Destruction Callbacks
- Lifecycle Method Order
- BeanPostProcessor
- Circular Dependencies
- Practical Mini Application
- Advanced Notes
- Certification Traps
- Interview Questions and Answers
- Quick Revision Notes
- One-Page Cheat Sheet
1. Big Picture
Spring is built around the IoC container.
IoC means Inversion of Control: instead of your code creating and wiring objects manually, Spring creates objects, connects dependencies, manages lifecycle callbacks, and provides ready-to-use instances.
In Spring, managed objects are called beans.
Without Spring
OrderService
|
| creates manually
v
JdbcOrderRepository
With Spring
Spring Container
|
| creates and wires
v
OrderService ---> OrderRepository
2. Dependency Injection
Dependency Injection is the process where required dependencies are provided to a class from outside instead of being created inside the class.
2.1 Problem Without Dependency Injection
public class OrderService {
private final OrderRepository orderRepository = new JdbcOrderRepository();
public void placeOrder(Order order) {
orderRepository.save(order);
}
}
Problems:
OrderServiceis tightly coupled toJdbcOrderRepository.- Testing is difficult because a mock repository cannot be easily supplied.
- Replacing JDBC with JPA, MongoDB, or an external API requires changing service code.
2.2 Solution With Dependency Injection
public interface OrderRepository {
void save(Order order);
}
@Repository
public class JdbcOrderRepository implements OrderRepository {
@Override
public void save(Order order) {
System.out.println("Saving order using JDBC");
}
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
public OrderService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public void placeOrder(Order order) {
orderRepository.save(order);
}
}
Now OrderService depends on the abstraction OrderRepository, not a concrete implementation.
3. Spring Bean Basics
A bean is an object managed by the Spring container.
Common ways to define beans:
- Stereotype annotations:
@Component,@Service,@Repository,@Controller - Java configuration:
@Bean - XML configuration, mostly legacy
3.1 Component-Based Bean
@Component
public class EmailNotificationService {
public void send(String message) {
System.out.println("Email sent: " + message);
}
}
3.2 Java Configuration Bean
@Configuration
public class AppConfig {
@Bean
public Clock clock() {
return Clock.systemUTC();
}
}
The method name clock becomes the bean name unless explicitly changed.
@Bean("utcClock")
public Clock clock() {
return Clock.systemUTC();
}
4. Spring Container
The container:
- Scans bean definitions.
- Creates bean instances.
- Injects dependencies.
- Applies bean lifecycle callbacks.
- Manages bean scopes.
- Destroys beans when appropriate.
Application starts
|
v
Spring scans configuration
|
v
Bean definitions registered
|
v
Beans instantiated
|
v
Dependencies injected
|
v
Lifecycle callbacks executed
|
v
Application uses beans
|
v
Container shutdown
5. Constructor Injection
Constructor injection provides dependencies through a class constructor.
@Service
public class PaymentService {
private final PaymentGateway paymentGateway;
public PaymentService(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
public void pay(BigDecimal amount) {
paymentGateway.charge(amount);
}
}
Why Constructor Injection Is Preferred
- Dependencies are mandatory.
- Object is created in a valid state.
- Supports immutability with
finalfields. - Easier to test.
- Avoids hidden dependencies.
- Detects many circular dependencies early.
Single Constructor Rule
In modern Spring, if a bean has only one constructor, @Autowired is optional.
@Service
public class ReportService {
private final ReportRepository repository;
public ReportService(ReportRepository repository) {
this.repository = repository;
}
}
This works without @Autowired.
Multiple Constructors
If there are multiple constructors, Spring needs to know which constructor to use.
@Service
public class ReportService {
private final ReportRepository repository;
private final AuditService auditService;
public ReportService(ReportRepository repository) {
this.repository = repository;
this.auditService = null;
}
@Autowired
public ReportService(ReportRepository repository, AuditService auditService) {
this.repository = repository;
this.auditService = auditService;
}
}
6. Setter Injection
Setter injection provides dependencies using setter methods.
@Service
public class InvoiceService {
private TaxService taxService;
@Autowired
public void setTaxService(TaxService taxService) {
this.taxService = taxService;
}
}
When Setter Injection Is Useful
- Optional dependencies.
- Reconfigurable dependencies.
- Framework integration requiring no-argument constructors.
- Breaking certain circular dependencies, although this should be a last resort.
Risk of Setter Injection
The object can exist in an incomplete state if a required dependency is not set.
public class InvoiceService {
private TaxService taxService;
public BigDecimal total(Invoice invoice) {
return taxService.calculate(invoice);
}
}
If taxService is not injected, this causes a NullPointerException.
7. Field Injection
Field injection injects dependencies directly into fields.
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
}
Field injection works, but it is usually discouraged.
Problems:
- Harder to test without Spring.
- Cannot use
final. - Dependencies are hidden.
- Object can be constructed manually in an invalid state.
- Reflection is required.
Certification trap: field injection is valid Spring, but constructor injection is generally preferred.
8. Injection Type Comparison
| Injection Type | Best For | Pros | Cons |
|---|---|---|---|
| Constructor injection | Required dependencies | Immutable, testable, safe | Can expose too many dependencies |
| Setter injection | Optional dependencies | Flexible | Object can be partially initialized |
| Field injection | Simple demos, legacy code | Concise | Hidden dependencies, harder testing |
9. Autowiring Resolution
When Spring sees a dependency, it resolves it using type information first.
@Service
public class CheckoutService {
private final PaymentProcessor paymentProcessor;
public CheckoutService(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
}
If there is exactly one PaymentProcessor bean, Spring injects it.
public interface PaymentProcessor {
void process(BigDecimal amount);
}
@Component
public class CardPaymentProcessor implements PaymentProcessor {
@Override
public void process(BigDecimal amount) {
System.out.println("Card payment");
}
}
10. Multiple Bean Candidates
If multiple beans of the same type exist, Spring cannot choose automatically.
@Component
public class CardPaymentProcessor implements PaymentProcessor {
}
@Component
public class UpiPaymentProcessor implements PaymentProcessor {
}
@Service
public class CheckoutService {
public CheckoutService(PaymentProcessor paymentProcessor) {
}
}
This fails with NoUniqueBeanDefinitionException.
11. Qualifiers
@Qualifier tells Spring exactly which bean to inject.
@Service
public class CheckoutService {
private final PaymentProcessor paymentProcessor;
public CheckoutService(
@Qualifier("cardPaymentProcessor") PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
}
By default, component bean names are derived from class names with the first character lowercased:
CardPaymentProcessor -> cardPaymentProcessor
UpiPaymentProcessor -> upiPaymentProcessor
Custom Bean Name
@Component("cardProcessor")
public class CardPaymentProcessor implements PaymentProcessor {
}
public CheckoutService(@Qualifier("cardProcessor") PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
Qualifier With @Bean
@Configuration
public class PaymentConfig {
@Bean
public PaymentProcessor cardProcessor() {
return new CardPaymentProcessor();
}
@Bean
public PaymentProcessor upiProcessor() {
return new UpiPaymentProcessor();
}
}
public CheckoutService(@Qualifier("upiProcessor") PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
12. @Primary
@Primary marks one bean as the default candidate when multiple beans match by type.
@Component
@Primary
public class CardPaymentProcessor implements PaymentProcessor {
}
@Component
public class UpiPaymentProcessor implements PaymentProcessor {
}
Now Spring injects CardPaymentProcessor when no qualifier is provided.
@Qualifier Beats @Primary
public CheckoutService(@Qualifier("upiPaymentProcessor") PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
Even if CardPaymentProcessor is @Primary, the qualifier selects UpiPaymentProcessor.
13. Bean Scopes
Bean scope controls how many instances Spring creates and how long they live.
| Scope | Instances | Available In | Description |
|---|---|---|---|
singleton | One per Spring container | All Spring apps | Default scope |
prototype | New instance per request from container | All Spring apps | Container creates but does not fully manage destruction |
request | One per HTTP request | Web apps | Valid during a web request |
session | One per HTTP session | Web apps | Valid during an HTTP session |
application | One per ServletContext | Web apps | Shared across servlet application |
websocket | One per WebSocket session | Web apps | Valid during WebSocket session |
14. Singleton Scope
Singleton is the default Spring bean scope.
@Service
public class CurrencyService {
}
Equivalent to:
@Service
@Scope("singleton")
public class CurrencyService {
}
Spring Container
|
+-- currencyService instance 1
+-- orderService uses same currencyService
+-- invoiceService uses same currencyService
Important: Spring singleton is one instance per Spring container, not necessarily one instance per JVM.
15. Prototype Scope
Prototype scope creates a new bean instance every time the bean is requested from the container.
@Component
@Scope("prototype")
public class TrackingIdGenerator {
private final UUID id = UUID.randomUUID();
public UUID getId() {
return id;
}
}
TrackingIdGenerator first = context.getBean(TrackingIdGenerator.class);
TrackingIdGenerator second = context.getBean(TrackingIdGenerator.class);
System.out.println(first == second); // false
Certification trap: Spring calls initialization callbacks for prototype beans, but it does not manage their full destruction lifecycle.
16. Singleton Bean Depending on Prototype Bean
This is a common trap.
@Service
public class ReportService {
private final TrackingIdGenerator generator;
public ReportService(TrackingIdGenerator generator) {
this.generator = generator;
}
}
If ReportService is singleton and TrackingIdGenerator is prototype, the prototype dependency is injected only once when the singleton is created.
Container startup
|
v
Creates singleton ReportService
|
v
Injects one prototype TrackingIdGenerator
|
v
Same generator reused inside ReportService
Solution 1: ObjectProvider
@Service
public class ReportService {
private final ObjectProvider<TrackingIdGenerator> generatorProvider;
public ReportService(ObjectProvider<TrackingIdGenerator> generatorProvider) {
this.generatorProvider = generatorProvider;
}
public void createReport() {
TrackingIdGenerator generator = generatorProvider.getObject();
System.out.println(generator.getId());
}
}
Solution 2: @Lookup
@Service
public abstract class ReportService {
public void createReport() {
TrackingIdGenerator generator = trackingIdGenerator();
System.out.println(generator.getId());
}
@Lookup
protected abstract TrackingIdGenerator trackingIdGenerator();
}
17. Web Scopes
Request Scope
@Component
@RequestScope
public class RequestContext {
private String correlationId;
public String getCorrelationId() {
return correlationId;
}
public void setCorrelationId(String correlationId) {
this.correlationId = correlationId;
}
}
One instance is created for each HTTP request.
Session Scope
@Component
@SessionScope
public class ShoppingCart {
private final List<String> items = new ArrayList<>();
public void add(String item) {
items.add(item);
}
}
One instance is created for each HTTP session.
Scoped Proxy
A singleton bean cannot directly hold a normal request-scoped bean because the request bean does not exist at application startup.
@Component
@RequestScope
public class RequestContext {
}
@Service
public class AuditService {
private final RequestContext requestContext;
public AuditService(RequestContext requestContext) {
this.requestContext = requestContext;
}
}
Spring may need a proxy:
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
}
Singleton AuditService
|
v
RequestContext proxy
|
v
Actual RequestContext for current HTTP request
18. Bean Lifecycle
Bean lifecycle means the sequence of steps from bean creation to destruction.
1. Bean definition loaded
2. Bean instantiated
3. Dependencies injected
4. Aware callbacks invoked
5. BeanPostProcessor before initialization
6. Initialization callbacks
7. BeanPostProcessor after initialization
8. Bean ready for use
9. Destruction callbacks on shutdown
19. Lifecycle Diagram
Spring Container
|
v
Instantiate bean
|
v
Populate properties / inject dependencies
|
v
BeanNameAware / BeanFactoryAware / ApplicationContextAware
|
v
BeanPostProcessor before initialization
|
v
@PostConstruct
|
v
InitializingBean.afterPropertiesSet()
|
v
custom init-method
|
v
BeanPostProcessor after initialization
|
v
Bean ready
|
v
@PreDestroy
|
v
DisposableBean.destroy()
|
v
custom destroy-method
20. Initialization Callbacks
@PostConstruct
@Component
public class CacheLoader {
@PostConstruct
public void loadCache() {
System.out.println("Loading cache");
}
}
@PostConstruct runs after dependency injection is complete.
InitializingBean
@Component
public class CacheLoader implements InitializingBean {
@Override
public void afterPropertiesSet() {
System.out.println("Loading cache");
}
}
This couples the class to Spring.
Custom Init Method
@Configuration
public class CacheConfig {
@Bean(initMethod = "start")
public CacheClient cacheClient() {
return new CacheClient();
}
}
public class CacheClient {
public void start() {
System.out.println("Starting cache client");
}
}
21. Destruction Callbacks
@PreDestroy
@Component
public class ConnectionPool {
@PreDestroy
public void close() {
System.out.println("Closing connections");
}
}
DisposableBean
@Component
public class ConnectionPool implements DisposableBean {
@Override
public void destroy() {
System.out.println("Closing connections");
}
}
Custom Destroy Method
@Configuration
public class ConnectionConfig {
@Bean(destroyMethod = "shutdown")
public ConnectionPool connectionPool() {
return new ConnectionPool();
}
}
22. Lifecycle Method Order
If several initialization mechanisms are present, the order is:
1. @PostConstruct
2. InitializingBean.afterPropertiesSet()
3. custom init-method
For destruction:
1. @PreDestroy
2. DisposableBean.destroy()
3. custom destroy-method
23. BeanPostProcessor
BeanPostProcessor allows custom logic before and after bean initialization.
@Component
public class LoggingBeanPostProcessor 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 real-world uses:
- Creating proxies.
- Processing annotations.
- Adding metrics or logging.
- Supporting framework features such as
@Autowired.
24. Circular Dependencies
A circular dependency occurs when two or more beans depend on each other.
ServiceA ---> ServiceB
^ |
| v
+-----------+
24.1 Constructor-Based Circular Dependency
@Service
public class ServiceA {
public ServiceA(ServiceB serviceB) {
}
}
@Service
public class ServiceB {
public ServiceB(ServiceA serviceA) {
}
}
This usually fails because neither bean can be created first.
Create ServiceA
|
v
Needs ServiceB
|
v
Create ServiceB
|
v
Needs ServiceA
|
v
Failure
24.2 Setter-Based Circular Dependency
Setter injection may allow some circular dependencies because Spring can instantiate objects first and inject dependencies later.
@Service
public class ServiceA {
private ServiceB serviceB;
@Autowired
public void setServiceB(ServiceB serviceB) {
this.serviceB = serviceB;
}
}
@Service
public class ServiceB {
private ServiceA serviceA;
@Autowired
public void setServiceA(ServiceA serviceA) {
this.serviceA = serviceA;
}
}
This is not a design recommendation. It is a workaround.
24.3 Better Fix: Redesign Responsibilities
Bad design:
OrderService <----> PaymentService
Better design:
OrderService ---> PaymentService
|
v
OrderStatusUpdater
Or use events:
OrderService publishes OrderPlacedEvent
|
v
PaymentListener handles payment
public record OrderPlacedEvent(Long orderId) {
}
@Service
public class OrderService {
private final ApplicationEventPublisher publisher;
public OrderService(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void placeOrder(Long orderId) {
publisher.publishEvent(new OrderPlacedEvent(orderId));
}
}
@Component
public class PaymentListener {
@EventListener
public void onOrderPlaced(OrderPlacedEvent event) {
System.out.println("Taking payment for order " + event.orderId());
}
}
24.4 @Lazy for Circular Dependencies
@Lazy can delay dependency resolution.
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}
}
Use this carefully. It can hide a design problem.
25. Practical Mini Application
Requirement
Create an order checkout flow with:
CheckoutService- Two payment processors
- Constructor injection
- Qualifier selection
- Lifecycle callback
Code
public interface PaymentProcessor {
void process(BigDecimal amount);
}
@Component("cardProcessor")
public class CardPaymentProcessor implements PaymentProcessor {
@Override
public void process(BigDecimal amount) {
System.out.println("Paid by card: " + amount);
}
}
@Component("upiProcessor")
public class UpiPaymentProcessor implements PaymentProcessor {
@PostConstruct
public void init() {
System.out.println("UPI processor initialized");
}
@PreDestroy
public void shutdown() {
System.out.println("UPI processor shutting down");
}
@Override
public void process(BigDecimal amount) {
System.out.println("Paid by UPI: " + amount);
}
}
@Service
public class CheckoutService {
private final PaymentProcessor paymentProcessor;
public CheckoutService(@Qualifier("upiProcessor") PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void checkout(BigDecimal amount) {
paymentProcessor.process(amount);
}
}
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
CheckoutService checkoutService = context.getBean(CheckoutService.class);
checkoutService.checkout(new BigDecimal("999.00"));
context.close();
}
}
Expected output:
UPI processor initialized
Paid by UPI: 999.00
UPI processor shutting down
26. Advanced Notes
26.1 Dependency Injection Is Not Only @Autowired
Spring can inject dependencies through:
- Constructor parameters
- Setter methods
- Fields
@Beanmethod parameters
@Configuration
public class AppConfig {
@Bean
public CheckoutService checkoutService(PaymentProcessor paymentProcessor) {
return new CheckoutService(paymentProcessor);
}
}
26.2 Optional Dependencies
Using Optional:
@Service
public class NotificationService {
public NotificationService(Optional<SmsClient> smsClient) {
smsClient.ifPresent(client -> System.out.println("SMS enabled"));
}
}
Using ObjectProvider:
@Service
public class NotificationService {
private final ObjectProvider<SmsClient> smsClientProvider;
public NotificationService(ObjectProvider<SmsClient> smsClientProvider) {
this.smsClientProvider = smsClientProvider;
}
public void notifyUser(String message) {
SmsClient smsClient = smsClientProvider.getIfAvailable();
if (smsClient != null) {
smsClient.send(message);
}
}
}
26.3 Injecting All Beans of a Type
@Service
public class PaymentRouter {
private final List<PaymentProcessor> processors;
public PaymentRouter(List<PaymentProcessor> processors) {
this.processors = processors;
}
}
Spring injects all beans implementing PaymentProcessor.
26.4 Injecting a Map of Beans
@Service
public class PaymentRouter {
private final Map<String, PaymentProcessor> processors;
public PaymentRouter(Map<String, PaymentProcessor> processors) {
this.processors = processors;
}
public void pay(String type, BigDecimal amount) {
processors.get(type).process(amount);
}
}
The map keys are bean names.
27. Certification Traps
| Trap | Correct Understanding |
|---|---|
| Spring singleton means one object in the JVM. | It means one instance per Spring container. |
| Prototype beans are destroyed automatically by Spring. | Spring initializes prototypes but does not fully manage their destruction. |
@Autowired is always required on constructors. | It is optional when there is only one constructor. |
| Field injection is invalid. | It is valid but usually discouraged. |
@Qualifier and @Primary have equal priority. | @Qualifier is more specific and wins. |
| A prototype bean injected into a singleton gives a new instance every method call. | It gives one prototype instance at singleton creation time unless provider or lookup is used. |
@PostConstruct runs before dependency injection. | It runs after dependency injection. |
| Constructor circular dependencies are easy for Spring to resolve. | They usually fail because both beans need each other during construction. |
@Service, @Repository, and @Controller are unrelated to @Component. | They are specialized forms of @Component. |
| Bean name and class name are always identical. | Default bean name usually starts with lowercase class name. |
28. Interview Questions and Answers
1. What is Dependency Injection?
Dependency Injection is a design pattern where an object's dependencies are supplied from outside instead of being created inside the object. In Spring, the container performs this wiring.
2. Why is constructor injection preferred?
It makes dependencies explicit, supports immutability, ensures required dependencies are available at creation time, and improves testability.
3. What is a Spring bean?
A Spring bean is an object created, configured, and managed by the Spring IoC container.
4. What is the default bean scope in Spring?
The default scope is singleton.
5. Is Spring singleton the same as the Singleton design pattern?
No. The Singleton design pattern usually means one instance per classloader or JVM. Spring singleton means one instance per Spring container.
6. What happens when multiple beans match the same dependency type?
Spring throws NoUniqueBeanDefinitionException unless one bean is selected using @Primary, @Qualifier, bean name matching, or another resolution mechanism.
7. What is the difference between @Primary and @Qualifier?
@Primary marks a default bean. @Qualifier explicitly selects a bean and has higher priority.
8. What is a circular dependency?
A circular dependency occurs when two or more beans depend on each other directly or indirectly.
9. How can circular dependencies be fixed?
The best fix is redesigning responsibilities. Other options include using events, splitting services, setter injection, ObjectProvider, or @Lazy.
10. What is the difference between singleton and prototype scope?
Singleton creates one bean instance per container. Prototype creates a new instance whenever requested from the container.
11. Does Spring call destroy methods on prototype beans?
Generally no. Spring creates and initializes prototype beans but does not manage their complete destruction lifecycle.
12. What is BeanPostProcessor?
It is an extension point that allows custom logic before and after bean initialization.
13. When does @PostConstruct run?
It runs after the bean is instantiated and dependencies are injected, but before the bean is used.
14. What is setter injection best suited for?
Setter injection is best suited for optional or reconfigurable dependencies.
15. How do you inject all implementations of an interface?
Use List<InterfaceType> or Map<String, InterfaceType> in the constructor.
29. Quick Revision Notes
- Spring IoC container creates and manages beans.
- Dependency Injection reduces tight coupling.
- Constructor injection is preferred for required dependencies.
- Setter injection is useful for optional dependencies.
- Field injection is valid but discouraged.
singletonis the default bean scope.- Spring singleton means one instance per container.
- Prototype scope creates a new instance when requested from the container.
- Prototype beans injected into singleton beans are not refreshed automatically.
- Use
ObjectProvideror@Lookupfor fresh prototype instances inside singletons. @Qualifierselects a specific bean.@Primaryselects the default bean when multiple candidates exist.@Qualifieroverrides@Primary.@PostConstructruns after dependency injection.@PreDestroyruns before bean destruction.- Constructor circular dependencies usually fail.
- Circular dependencies often indicate poor design.
- Prefer redesign, domain events, or responsibility separation over circular wiring.
30. One-Page Cheat Sheet
Core Terms
| Term | Meaning |
|---|---|
| IoC | Control of object creation is moved to the Spring container |
| DI | Dependencies are supplied from outside |
| Bean | Object managed by Spring |
| Scope | Lifecycle and instance creation rule for a bean |
| Autowiring | Spring automatically resolves and injects dependencies |
Injection Choices
| Use Case | Recommended Injection |
|---|---|
| Required dependency | Constructor injection |
| Optional dependency | Setter injection or ObjectProvider |
| Quick demo or legacy code | Field injection |
| Multiple implementations | Constructor injection with @Qualifier |
Bean Scopes
| Scope | Instance Rule |
|---|---|
singleton | One instance per Spring container |
prototype | New instance per container request |
request | One instance per HTTP request |
session | One instance per HTTP session |
application | One instance per servlet context |
websocket | One instance per WebSocket session |
Candidate Selection
Spring resolves by type
|
v
One candidate? inject it
|
v
Multiple candidates?
|
+-- @Qualifier present? use it
|
+-- @Primary present? use it
|
+-- parameter name matches bean name? may use it
|
+-- otherwise fail
Lifecycle Order
Instantiate
Inject dependencies
Aware callbacks
BeanPostProcessor before init
@PostConstruct
InitializingBean.afterPropertiesSet()
custom init-method
BeanPostProcessor after init
Bean ready
@PreDestroy
DisposableBean.destroy()
custom destroy-method
Must-Remember Exam Points
@Autowiredis optional on a single constructor.@Qualifieris more specific than@Primary.@PostConstructhappens after dependency injection.- Prototype destruction is not fully managed by Spring.
- Constructor injection exposes circular dependencies early.
- Setter injection can sometimes resolve circular dependencies but is not the preferred design fix.
- A singleton bean receives a prototype dependency only once unless lookup or provider-based access is used.