Dependency Injection & Bean Lifecycle
Master Spring IoC container. Learn bean definitions, autowiring, component scopes, circular dependencies, and lifecycle callbacks.
Downloaded from PrepIQ (https://prepiq.online)
Q1. What is Dependency Injection and what problem does it solve?
Dependency Injection (DI) is a design pattern where an object's dependencies are supplied by an external source (the Spring IoC container) rather than the object creating them itself with new. Instead of a class instantiating its own collaborators, those collaborators are "injected" through the constructor, a setter, or a field.
DI solves tight coupling: without it, classes are hard-wired to specific implementations, making unit testing difficult (you can't easily substitute a mock) and changes ripple across the codebase. With DI, classes depend on abstractions, implementations can be swapped without touching consumer code, and testing becomes straightforward since dependencies can be mocked and injected directly.
@Service
public class OrderService {
private final PaymentGateway paymentGateway; // injected, not "new"ed
public OrderService(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
}
Q2. What is Inversion of Control (IoC) and how does the Spring IoC Container work?
Inversion of Control is the broader principle behind DI: control over object creation and wiring is inverted from the application code to a framework/container. Instead of a class controlling when and how its dependencies are constructed, the Spring IoC Container owns that responsibility.
The container, represented by ApplicationContext, reads bean definitions (from annotations, Java config, or XML), instantiates beans, resolves their dependencies, and manages their full lifecycle. This is why Spring applications are described as having their control "inverted" — the framework calls your code, not the other way around.
ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
OrderService orderService = context.getBean(OrderService.class);
Q3. What is the difference between Constructor Injection, Setter Injection, and Field Injection?
Constructor Injection supplies dependencies through a class constructor, making them final and guaranteeing the object is never in a partially-constructed, invalid state. Setter Injection supplies dependencies through public setter methods after construction, useful for optional dependencies. Field Injection uses @Autowired directly on a field, which is the most concise but hardest to test and hides dependencies from the constructor signature.
Constructor Injection is the officially recommended approach by the Spring team: it enables immutability (final fields), makes required dependencies explicit and impossible to forget, works well with unit testing without a Spring context, and surfaces circular dependencies at startup instead of silently working around them.
// Recommended: Constructor Injection
@Service
public class OrderService {
private final PaymentGateway paymentGateway;
public OrderService(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
}
// Discouraged: Field Injection
@Service
public class OrderService {
@Autowired
private PaymentGateway paymentGateway;
}
Q4. What are Spring Bean Scopes (singleton, prototype, request, session)?
Bean scope defines how many instances of a bean the container creates and how long they live. singleton (the default) creates exactly one shared instance per Spring container, reused for every injection point. prototype creates a new instance every time the bean is requested from the container.
Web-aware scopes are also available when using a web application context: request creates one instance per HTTP request, session creates one instance per HTTP session, and application ties an instance to the ServletContext lifecycle. Choosing the wrong scope is a common source of bugs — e.g. injecting a prototype bean into a singleton without a scoped proxy means the prototype is only created once, defeating its purpose.
@Component
@Scope("prototype")
public class ShoppingCart { ... }
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext { ... }
Q5. What is the Spring Bean Lifecycle, and what are its key phases?
The Spring Bean Lifecycle describes the sequence of steps the container performs to create, initialize, and eventually destroy a bean. In order: the container instantiates the bean, injects its dependencies, calls Aware interface setters (e.g. BeanNameAware) if implemented, applies BeanPostProcessor.postProcessBeforeInitialization(), invokes @PostConstruct or afterPropertiesSet() (from InitializingBean), applies postProcessAfterInitialization(), and then the bean is ready for use.
On container shutdown, @PreDestroy or destroy() (from DisposableBean) is called for singleton beans, giving them a chance to release resources like connections or file handles. Prototype beans are not tracked for destruction by the container once handed off.
@Component
public class CacheManager implements InitializingBean, DisposableBean {
@PostConstruct
public void init() { /* load cache */ }
@PreDestroy
public void cleanup() { /* release resources */ }
}
Q6. What is the difference between @Component, @Service, @Repository, and @Controller?
All four are stereotype annotations built on top of @Component, so all are detected by component scanning and registered as beans — functionally, Spring treats them the same way for the purpose of bean registration. The distinction is semantic and enables extra framework behavior for each layer.
@Repository additionally enables automatic translation of persistence-related exceptions into Spring's unified DataAccessException hierarchy. @Service marks business/service-layer logic (behaves like plain @Component, mainly documentation-level distinction). @Controller marks a Spring MVC web controller that returns view names, and @RestController (which combines @Controller + @ResponseBody) marks a REST endpoint. Using the correct stereotype communicates architectural intent and unlocks layer-specific tooling like AOP pointcuts targeting @Service classes.
@Repository public interface UserRepository extends JpaRepository<User, Long> {}
@Service public class UserService { }
@RestController public class UserController { }
Q7. What is a Circular Dependency in Spring and how do you resolve it?
A circular dependency occurs when Bean A depends on Bean B, and Bean B depends back on Bean A (directly or through a longer chain), creating a cycle the container can't resolve through straightforward constructor injection since neither bean can be fully constructed first.
With constructor injection, Spring throws a BeanCurrentlyInCreationException at startup, surfacing the design problem immediately. Common fixes include refactoring to remove the cycle (usually the correct fix — it often signals a design smell), switching one side to setter/field injection so Spring can inject after both beans partially exist, or using @Lazy on one of the dependencies so it's only resolved on first use rather than at construction time.
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(@Lazy ServiceB serviceB) { this.serviceB = serviceB; }
}
Q8. What is @Qualifier and when do you need it?
When multiple beans of the same type exist in the container, @Autowired alone can't determine which one to inject, causing a NoUniqueBeanDefinitionException. @Qualifier("beanName") resolves the ambiguity by specifying exactly which bean implementation should be wired in, matched against the bean's name or an explicit qualifier value.
An alternative to @Qualifier is marking one implementation with @Primary so it becomes the default choice whenever multiple candidates exist, without needing to qualify every injection point — @Qualifier is preferred when you need to select different implementations at different injection points.
public interface NotificationService { void send(String msg); }
@Service("emailNotification")
public class EmailNotificationService implements NotificationService { ... }
@Service("smsNotification")
public class SmsNotificationService implements NotificationService { ... }
@Service
public class OrderService {
public OrderService(@Qualifier("emailNotification") NotificationService service) { ... }
}
Q9. What is @Bean vs @Component — when do you use each?
@Component (and its stereotypes like @Service) is a class-level annotation used when you own the source code — Spring detects the annotated class during component scanning and registers it as a bean automatically.
@Bean is a method-level annotation used inside a @Configuration class, giving explicit, programmatic control over how a bean is constructed — essential for registering beans from third-party libraries you don't own and can't annotate, for beans that need custom construction logic, or for conditionally creating beans based on properties or profiles.
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.setConnectTimeout(Duration.ofSeconds(5)).build();
}
}
Q10. What is Autowiring by Type vs by Name, and what happens when Spring can't resolve a dependency?
By default, @Autowired resolves dependencies by type — Spring looks for a bean matching the field or parameter's declared type. If exactly one candidate exists, it's injected directly; if none exist, Spring throws NoSuchBeanDefinitionException at startup (unless the dependency is marked required = false or wrapped in Optional).
If multiple beans of the same type exist, Spring falls back to matching by field/parameter name against bean names before giving up with a NoUniqueBeanDefinitionException — this is effectively "autowiring by name" as a tiebreaker, though it's less explicit and reliable than using @Qualifier or @Primary to disambiguate directly. Failing fast at startup rather than at runtime is one of Spring's key reliability guarantees for dependency wiring.
@Autowired(required = false)
private AnalyticsService analyticsService; // optional dependency, no exception if missing
Dependency Injection & Bean Lifecycle
Master Spring IoC container. Learn bean definitions, autowiring, component scopes, circular dependencies, and lifecycle callbacks.
What is Dependency Injection and what problem does it solve?
Dependency Injection (DI) is a design pattern where an object's dependencies are supplied by an external sourc...
What is Inversion of Control (IoC) and how does the Spring IoC Container work?
Inversion of Control is the broader principle behind DI: control over object creation and wiring is inverted f...
What is the difference between Constructor Injection, Setter Injection, and Field Injection?
Constructor Injection supplies dependencies through a class constructor, making them final and guaranteeing th...
What are Spring Bean Scopes (singleton, prototype, request, session)?
Bean scope defines how many instances of a bean the container creates and how long they live. singleton (the d...
What is the Spring Bean Lifecycle, and what are its key phases?
The Spring Bean Lifecycle describes the sequence of steps the container performs to create, initialize, and ev...
What is the difference between @Component, @Service, @Repository, and @Controller?
All four are stereotype annotations built on top of @Component, so all are detected by component scanning and...
What is a Circular Dependency in Spring and how do you resolve it?
A circular dependency occurs when Bean A depends on Bean B, and Bean B depends back on Bean A (directly or thr...
What is @Qualifier and when do you need it?
When multiple beans of the same type exist in the container, @Autowired alone can't determine which one to inj...
What is @Bean vs @Component — when do you use each?
@Component (and its stereotypes like @Service) is a class-level annotation used when you own the source code —...
What is Autowiring by Type vs by Name, and what happens when Spring can't resolve a dependency?
By default, @Autowired resolves dependencies by type — Spring looks for a bean matching the field or parameter...