객체지향 설계 원칙 중 OCP(Open-Closed Principle), DIP(Dependency Inversion Principle)를 지키기 위해서는 구현체에 의존하지 않고, 인터페이스에 의존하면서도, 객체 생성과 연결을 유연하게 처리해야 합니다.
public class OrderService {
private final MemberService memberService = new MemberServiceImpl();
}
의존성 주입을 통해 객체 생성과 연결을 외부로 분리하고, 객체 간의 결합도를 낮춰 유지보수성과 확장성을 확보합니다.
그렇다면 의존성 주입에서 스프링은 무슨 역할을 할까요?

객체를 생성하고 관리하는 중앙 저장소이자 제어 주체입니다.
ApplicationContext컨테이너 내부에는 객체들이 빈(Bean)으로 저장되어 관리됩니다.
스프링이 직접 생성하고 관리하는 객체입니다.
@Component, @Configuration, @Bean 등으로 등록된 객체등록된 빈은 getBean() 또는 @Autowired 등을 통해 주입됩니다.
스프링 프로젝트를 생성하면 기본적으로 Application 클래스가 존재합니다.
클래스에는 @SpringBootApplication가 붙어있고, 해당 어노테이션의 메타 어노테이션으로 @ComponentScan이 붙어 @Component를 자동으로 컨테이너에 등록해줍니다.
@Configuration의 경우 메타 어노테이션에 @Component가 붙어 있어서 마찬가지로 자동으로 컨테이너에 등록해줍니다.
빈 생명주기는 다음과 같습니다.
스프링 IoC 컨테이너 생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 메서드 호출 → 빈 사용 → 소멸 전 콜백 메서드 호출 → 스프링 종료
스프링 빈은 객체를 생성한 후 의존관계가 모두 주입되어야 비로소 사용할 준비가 완료됩니다. 초기화 작업이 필요한 경우 스프링은 의존관계 주입이 완료된 시점에 자동으로 호출되는 콜백 메서드를 제공하여 개발자가 초기화 타이밍을 정확히 파악하고 로직을 수행할 수 있게 합니다.
또한, 스프링 컨테이너가 종료되기 직전에는 소멸 콜백 메서드를 통해 필요한 정리 작업을 안전하게 처리할 수 있도록 지원한다.
스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해서 초기화 시점을 알려주는 다양한 기능을 제공합니다.
스프링 컨테이너가 종료되기 직전에도 소멸 콜백을 주어 안전하게 종료작업을 진행할 수 있습니다.
public class MyBean implements InitializingBean, DisposableBean {
public void afterPropertiesSet() { ... }
public void destroy() { ... }
}
@Bean(initMethod = "init", destroyMethod = "cleanup")
public MyBean myBean() {
return new MyBean();
}
@PostConstruct
public void init() {
// 의존성 주입 완료 후 호출
}
@PreDestroy
public void cleanup() {
// 컨테이너 종료 직전 호출
}
빈이 생성되고 유지되는 범위를 지정하는 설정입니다.


@Component
public class OrderService {
private final MemberService memberService;
// 생성자 주입
@Autowired
public OrderService(MemberService memberService) {
this.memberService = memberService;
}
}
@Component
public class OrderService {
private MemberService memberService;
// Setter 주입
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
}
xml 파일, 어노테이션(@Bean)으로 스프링 컨테이너에 객체를 등록하면 스프링 컨테이너에서 객체의 생명주기를 관리합니다.
즉, 객체의 제어권이 컨테이너(스프링)으로 바뀌기 때문에 제어의 역전(IoC)라 부릅니다.
만약 IoC가 없다면 개발자가 직접 객체 생성, 의존성 설정 등등 제어해야 합니다.
스프링 빈을 조회하거나 관리하는 가장 기본적인 인터페이스입니다.
getBean() 메서드를 통해 빈을 이름 또는 타입으로 꺼낼 수 있습니다. 또 DI를 가능하게 해주는 핵심 기능을 포함합니다.
public interface BeanFactory {
Object getBean(String name) throws BeansException;
}
ApplicationContext는 BeanFactory를 상속한 상위 인터페이스입니다.
우리가 실무에서 자주 쓰는 @Autowired, @ComponentScan 기반 DI도 대부분 이 ApplicationContext에서 처리됩니다.
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
}