Spring Core is the foundation of the entire Spring ecosystem. Inversion of Control (IoC) and Dependency Injection (DI) flip the traditional model — instead of objects creating their own dependencies, the Spring container creates and injects them. This decoupling makes code testable, modular, and configurable.

Key Points

  • IoC Container: the ApplicationContext manages bean lifecycle — creation, wiring, and destruction
  • Dependency Injection types: Constructor injection (recommended), Setter injection, Field injection (@Autowired on field — avoid in production)
  • Constructor injection is preferred: dependencies are explicit, classes are immutable, and tests can be written without Spring
  • @Component / @Service / @Repository / @Controller: stereotype annotations that mark a class as a Spring-managed bean
  • @Bean: method-level annotation in a @Configuration class that declares a bean explicitly — used for third-party classes
  • Bean scopes: singleton (default — one per container), prototype (new instance per injection), request, session (web-aware)
  • Bean lifecycle: @PostConstruct (after properties set) → business use → @PreDestroy (before container closes)
  • @Autowired: Spring resolves dependency by type; use @Qualifier("name") or @Primary when multiple candidates exist
  • Bean lifecycle callbacks: InitializingBean/afterPropertiesSet() and DisposableBean/destroy() — prefer @PostConstruct/@PreDestroy
Spring IoC Container (ApplicationContext) UserController @RestController constructor-injected: UserService UserService @Service constructor-injected: UserRepository UserRepository @Repository constructor-injected: DataSource (bean) Bean Lifecycle 1. Instantiate 2. Inject deps 3. @PostConstruct 4. In service 5. @PreDestroy → destroy On context.close() or JVM shutdown

Spring IoC: container wires Controller → Service → Repository via constructor injection; bean lifecycle from instantiation to destruction

Spring Core: constructor injection, @Bean configuration, @PostConstruct/@PreDestroy lifecycle

// Constructor injection — preferred
@Service
public class UserService {
    private final UserRepository repo;
    private final EmailService emailService;

    // @Autowired optional when single constructor (Spring 4.3+)
    public UserService(UserRepository repo, EmailService emailService) {
        this.repo = repo;
        this.emailService = emailService;
    }
}

// @Configuration + @Bean — for third-party classes
@Configuration
public class AppConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplateBuilder()
            .setConnectTimeout(Duration.ofSeconds(5))
            .setReadTimeout(Duration.ofSeconds(10))
            .build();
    }
}

// Lifecycle callbacks
@Component
public class CacheWarmer {
    @PostConstruct
    public void warmUp() { loadCacheData(); }  // runs after DI complete

    @PreDestroy
    public void flush() { persistCacheToDb(); } // runs before shutdown
}

// Prototype scope — new instance per injection point
@Bean @Scope("prototype")
public ReportBuilder reportBuilder() { return new ReportBuilder(); }

Real-World Example

Field injection (@Autowired on a private field) is convenient but prevents testing without a Spring context and hides dependencies. Switching to constructor injection immediately reveals when a class has too many dependencies (more than 4–5 constructor params = a design smell suggesting the class needs to be split). Spring Boot auto-generates constructor injection for Lombok @RequiredArgsConstructor.