Dependency Injection
IoC vs DI, the IoC container, @Autowired, constructor vs setter vs field injection, @Qualifier, @Primary, the bean resolution algorithm, @Lazy, circular dependencies and ObjectProvider.
#Introduction
If there is one Spring topic every interviewer asks — from freshers to architects — it is Dependency Injection (DI). Almost every Spring Boot application uses @Autowired, constructor injection, @Qualifier, @Primary and @Lazy.
Interviewers commonly ask: What is DI? What is IoC? How does Spring create objects? What is bean resolution? What happens internally when Spring starts? How does @Autowired work? What happens if multiple beans exist? @Primary vs @Qualifier? How does @Lazy work? What causes circular dependencies?
#What is Dependency Injection?
Without DI
public class UserService {
private UserRepository repository = new UserRepository();
}Problems: tight coupling, difficult to test, hard to replace implementations, and it violates the Dependency Inversion Principle.
With DI
@Service
public class UserService {
private final UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
}Spring creates the repository and injects it.
#What is Inversion of Control (IoC)?
Many developers confuse IoC and DI.
Without Spring, your code does new UserRepository() and new UserService(repo) — your application controls object creation. With Spring, Spring controls object creation, not your code. This inversion of responsibility is called Inversion of Control.
IoC vs DI
| IoC | DI |
|---|---|
| Design principle | Implementation technique |
| Spring controls object creation | Spring injects dependencies |
| Bigger concept | One way to achieve IoC |
DI is one implementation of IoC.
#The Spring IoC Container
The IoC container is responsible for creating beans, managing bean lifecycle, injecting dependencies and destroying beans.
Main interfaces: BeanFactory and ApplicationContext. ApplicationContext extends BeanFactory and adds event publishing, internationalization (i18n), resource loading, the environment abstraction and bean post-processing support.
#What is @Autowired?
@Autowired tells Spring: find a suitable bean and inject it here.
@Service
public class UserService {
@Autowired
private UserRepository repository;
}How @Autowired Works Internally
#Injection Types
Constructor Injection (Recommended)
@Service
public class UserService {
private final UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
}Since Spring Framework 4.3, if a bean has a single constructor, @Autowired on it is optional.
Why constructor injection? Immutable dependencies (final), easier unit testing, mandatory dependencies, better design, and no partially initialized objects. The enterprise standard.
Setter Injection
@Service
public class UserService {
private UserRepository repository;
@Autowired
public void setRepository(UserRepository repository) {
this.repository = repository;
}
}Useful when the dependency is optional or can be changed.
Field Injection
@Service
public class UserService {
@Autowired
private UserRepository repository;
}Easy to write, but hard to unit test, hides dependencies, can't use final, and is discouraged in modern Spring applications.
Comparison
| Constructor | Setter | Field | |
|---|---|---|---|
| Recommendation | ✅ Recommended | For optional dependencies | ❌ Not recommended |
| Mutability | Immutable | Mutable | Mutable |
| Testing | Easy | Medium | Difficult |
Supports final | Yes | No | No |
#What If Multiple Beans Exist?
public interface PaymentService {
}
@Service
public class CreditCardService implements PaymentService {
}
@Service
public class UpiService implements PaymentService {
}
@Autowired
private PaymentService paymentService;Spring finds two beans:
NoUniqueBeanDefinitionException: expected single matching bean but found 2: creditCardService,upiService@Qualifier
Specify exactly which bean to inject:
@Service("upiService")
public class UpiService implements PaymentService {
}
@Autowired
@Qualifier("upiService")
private PaymentService paymentService;Spring injects only UpiService.
@Primary
@Service
@Primary
public class UpiService implements PaymentService {
}
@Autowired
private PaymentService paymentService; // UpiService — the default candidate@Primary vs @Qualifier
Interview favourite.
| @Primary | @Qualifier |
|---|---|
| Default bean | Specific bean |
| Global preference | Local selection |
| Used automatically | Explicitly requested |
Rule: @Qualifier wins over @Primary.
#Bean Resolution Algorithm
When Spring sees @Autowired PaymentService paymentService:
#@Lazy
Normally, when the application starts, all singleton beans are created. With @Lazy:
@Lazy
@Service
public class ReportService {
}Lazy initialization can reduce startup time but delays bean creation (and any configuration errors) until the bean is needed.
@Lazy at the Injection Point
@Service
public class UserService {
private final ReportService reportService;
public UserService(@Lazy ReportService reportService) {
this.reportService = reportService;
}
}Spring injects a proxy; the real bean is created only when first accessed.
#Circular Dependency
@Service
class UserService {
UserService(OrderService service) {}
}
@Service
class OrderService {
OrderService(UserService service) {}
}Result: BeanCurrentlyInCreationException — the dependencies of some of the beans form a cycle.
Solving Circular Dependencies
- Redesign responsibilities (best solution)
- Introduce a third service
- Use events or callbacks
- In some cases, use
@Lazy:
public OrderService(@Lazy UserService service) {
}@Lazy can break certain circular references by injecting a proxy, but redesigning the dependency graph is preferred.
#Injecting Collections and Maps
Spring can inject every implementation:
@Autowired
private List<PaymentService> services; // CreditCardService, UpiService, PaypalService@Autowired
private Map<String, PaymentService> services;upiService -> UpiService
creditCardService -> CreditCardServiceKeys are bean names — useful for strategy and plugin patterns.
#Optional Dependencies
@Autowired(required = false)
private AuditService auditService; // null if no bean existsModern alternative:
private final Optional<AuditService> auditService;
public UserService(Optional<AuditService> auditService) {
this.auditService = auditService;
}ObjectProvider
For lazy and optional access:
@Autowired
private ObjectProvider<PaymentService> provider;
PaymentService service = provider.getIfAvailable();Useful when bean creation is expensive or conditional.
#Internal Spring Components
Important internal classes: DefaultListableBeanFactory, AutowiredAnnotationBeanPostProcessor, DependencyDescriptor and ConstructorResolver — common discussion points in senior interviews.
#Common Mistakes
- Field injection everywhere — prefer constructor injection.
- Multiple beans without a qualifier — use
@Qualifier("upiService")or@Primary. - Using
@Primaryeverywhere — use it only when there's a clear default implementation. - Circular dependencies — fix the design; don't rely on
@Lazyas the default solution. - Manual object creation (
new UserRepository()) inside Spring-managed beans — let Spring manage creation and injection.
#Interview Questions
Q1What is Dependency Injection?
A design pattern where dependencies are provided by an external container instead of being created by the object itself.
Q2What is IoC?
Inversion of Control is the principle where the framework controls object creation and lifecycle rather than the application code.
Q3What is the difference between IoC and DI?
| IoC | DI |
|---|---|
| Principle | Technique |
| Controls the object lifecycle | Injects dependencies |
| Broad concept | Practical implementation |
Q4Which injection type is recommended?
Constructor injection, because it promotes immutability, testability and explicit dependencies.
Q5What happens if multiple beans of the same type exist?
Spring throws NoUniqueBeanDefinitionException, unless the choice is resolved using @Qualifier, @Primary or another qualifying mechanism.
Q6What is the difference between @Primary and @Qualifier?
@Primarydefines the default bean.@Qualifierexplicitly selects a specific bean.@Qualifiertakes precedence over@Primary.
Q7What is @Lazy?
It delays bean creation until the bean is first needed, typically by injecting a proxy.
Q8Which class processes @Autowired?
AutowiredAnnotationBeanPostProcessor.
Q9Which class performs dependency resolution?
DefaultListableBeanFactory.
Q10What is the bean resolution order?
Type match → @Qualifier → @Primary → name → exception.
#Key Takeaways
- DI reduces coupling by letting Spring provide dependencies.
- IoC is the broader principle; DI is Spring's primary implementation of it.
- Prefer constructor injection over field or setter injection.
- Use
@Qualifierwhen multiple beans exist;@Primarydefines a default. @Lazypostpones bean creation until first use and can break some cycles.DefaultListableBeanFactoryresolves dependencies;AutowiredAnnotationBeanPostProcessorprocesses@Autowired.- Know the bean resolution algorithm for senior interviews.