Skip to content

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):

@Service
public class OrderService {
    @Autowired
    private PaymentService paymentService;
    // Cannot be final, hidden dependency, needs reflection for tests
}


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:

@Service
public class OrderService {
    @Autowired
    private ObjectFactory<ShoppingCart> cartFactory;

    public void process() {
        ShoppingCart cart = cartFactory.getObject();  // New instance each time
    }
}


Bean Lifecycle

Bean lifecycle: InstantiationDI (populate properties)Aware interfacesBeanPostProcessor.before@PostConstructInitializingBeancustom initBeanPostProcessor.afterready to use@PreDestroyDisposableBeancustom 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
public class CacheService {
    @PostConstruct
    public void init() { /* Populate cache after DI */ }

    @PreDestroy
    public void cleanup() { /* Release resources */ }
}

@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 */ }
}
@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheManager cacheManager() { return new ConcurrentMapCacheManager(); }

@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager noOpCacheManager() { return new NoOpCacheManager(); }

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 @Component and @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 ApplicationContext and BeanFactory?
  • What are Spring profiles?
  • What is @PostConstruct? When is it called?