Spring Core & Dependency Injection¶
Spring Core is the foundation of the Spring Framework, built on IoC (Inversion of Control) and DI (Dependency Injection). Instead of objects creating their own dependencies, the Spring container manages object creation and wiring. DI can be done via constructor injection (preferred), setter injection, or field injection (@Autowired). The container uses BeanFactory or ApplicationContext to manage bean lifecycles.
┌────────────────────────────────────┐
│ ApplicationContext │
│ ┌──────────────────────────┐ │
│ │ Bean Definitions │ │
│ │ (@Component, @Bean, │ │
│ │ XML config) │ │
│ └──────────────────────────┘ │
│ ┌──────────────────────────┐ │
│ │ Bean Instances │ │
│ │ (Singletons, Prototypes │ │
│ │ etc.) │ │
│ └──────────────────────────┘ │
└────────────────────────────────────┘
Types of Dependency Injection¶
Constructor injection (preferred) — fields can be final, immutable, all dependencies visible, easy to test. Spring 4.3+ doesn't even need @Autowired for single constructors. Setter injection — for optional dependencies with defaults. Field injection (@Autowired on fields) — discouraged because it hides dependencies, prevents final, and requires reflection for testing.
Deep Dive: Injection Examples
1. Constructor Injection (Preferred):
@Service
public class OrderService {
private final PaymentService paymentService;
private final InventoryService inventoryService;
// @Autowired optional for single constructor (Spring 4.3+)
public OrderService(PaymentService paymentService,
InventoryService inventoryService) {
this.paymentService = paymentService;
this.inventoryService = inventoryService;
}
}
2. Setter Injection — for optional dependencies:
@Service
public class OrderService {
private PaymentService paymentService;
@Autowired
public void setPaymentService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
3. Field Injection (Discouraged):
Bean Scopes¶
Default scope is singleton (one instance per container). Prototype creates a new instance per injection. Web scopes: request, session, application. Gotcha: injecting a prototype into a singleton gives the same prototype instance every time — fix with ObjectFactory, Provider, or @Lookup.
Deep Dive: Scope Table & Prototype Gotcha
| Scope | Description | Default? |
|---|---|---|
singleton |
One instance per Spring container | ✅ Yes |
prototype |
New instance per injection/request | No |
request |
One per HTTP request | No (web) |
session |
One per HTTP session | No (web) |
application |
One per ServletContext |
No (web) |
Singleton + Prototype gotcha:
@Service // Singleton
public class OrderService {
@Autowired
private ShoppingCart cart; // Prototype — but same instance every time!
}
Fix — use ObjectFactory:
Bean Lifecycle¶
Bean lifecycle: Instantiation → DI (populate properties) → Aware interfaces → BeanPostProcessor.before → @PostConstruct → InitializingBean → custom init → BeanPostProcessor.after → ready to use → @PreDestroy → DisposableBean → custom destroy. In practice, use @PostConstruct for init and @PreDestroy for cleanup.
Deep Dive: Full Lifecycle
1. Instantiation (constructor)
2. Populate properties (DI)
3. BeanNameAware.setBeanName()
4. BeanFactoryAware.setBeanFactory()
5. ApplicationContextAware.setApplicationContext()
6. BeanPostProcessor.postProcessBeforeInitialization()
7. @PostConstruct
8. InitializingBean.afterPropertiesSet()
9. Custom init-method
10. BeanPostProcessor.postProcessAfterInitialization()
↓ (Bean is ready to use)
11. @PreDestroy
12. DisposableBean.destroy()
13. Custom destroy-method
@Component vs @Bean¶
@Component (and @Service, @Repository, @Controller) — annotate a class, detected via component scanning. @Bean — annotate a method in @Configuration, giving you full control over instantiation. Use @Bean for third-party classes you can't annotate, custom initialization, or conditional creation.
Deep Dive: Examples
@Service // Stereotype annotation — auto-detected
public class UserService { }
@Configuration
public class AppConfig {
@Bean // Manual bean — full control
public RestTemplate restTemplate() { return new RestTemplate(); }
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
return mapper;
}
}
@Service, @Repository, @Controller are functionally identical to @Component but provide semantic meaning and enable additional features (e.g., @Repository translates SQL exceptions).
@Qualifier and @Primary¶
When multiple beans of the same type exist, Spring can't auto-wire. @Qualifier("name") specifies which bean to inject. @Primary marks a default bean used when no qualifier is specified. @Qualifier overrides @Primary.
Deep Dive: Resolution Example
public interface NotificationService { void send(String msg); }
@Service("email")
public class EmailNotification implements NotificationService { ... }
@Primary
@Service("sms")
public class SmsNotification implements NotificationService { ... }
@Service
public class OrderService {
// Injects SmsNotification (@Primary) by default
public OrderService(NotificationService service) { ... }
// Injects EmailNotification (explicit @Qualifier)
public OrderService(@Qualifier("email") NotificationService service) { ... }
}
Profiles and Conditional Beans¶
Profiles activate beans based on environment (@Profile("dev")). Activated via spring.profiles.active=dev. @Conditional annotations provide fine-grained control: @ConditionalOnProperty, @ConditionalOnMissingBean, @ConditionalOnClass. Spring Boot auto-configuration is entirely built on @Conditional.
Deep Dive: Examples
@Profile("dev")
@Configuration
public class DevConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
}
@Profile("prod")
@Configuration
public class ProdConfig {
@Bean
public DataSource dataSource() { /* production datasource */ }
}
ApplicationContext vs BeanFactory¶
BeanFactory — basic container, lazy loading by default. ApplicationContext — extends BeanFactory, adds event publishing, AOP, internationalization, eager loading. Always use ApplicationContext unless extreme memory constraints apply.
Deep Dive: Comparison
| Feature | BeanFactory | ApplicationContext |
|---|---|---|
| Lazy loading | Yes (default) | No (eager by default) |
| Event publishing | No | Yes |
| AOP support | Basic | Full |
| Internationalization | No | Yes |
| Bean post-processing | Manual | Automatic |
Common Interview Questions¶
Common Interview Questions
- What is IoC? What is DI? How are they related?
- What are the different types of dependency injection?
- Why is constructor injection preferred?
- What are the different bean scopes in Spring?
- What happens when you inject a prototype bean into a singleton?
- Explain the bean lifecycle.
- What is the difference between
@Componentand@Bean? - What are
@Service,@Repository,@Controller— are they different from@Component? - How do you resolve ambiguity when multiple beans of the same type exist?
- What is the difference between
ApplicationContextandBeanFactory? - What are Spring profiles?
- What is
@PostConstruct? When is it called?