[Spring] IOC
해당 내용은 김영한씨의 강의 및 스프링 프레임워크 공식 문서를 번역하여 작성한 글입니다.
특히 공식 문서에 있어 올바르지 않은 번역 의견은 언제든지 환영합니다. 댓글 달아주세요
1. IOC(Inversion of Control)
제어의 역전 IoC(Inversion of Control) 기존 프로그램은 클라이언트 구현 객체가 스스로 필요한 서버 구현 객체를 생성하고, 연결하고, 실행했다. 한마디로 구현 객체가 프로그램의 제어 흐름을 스스로 조종했다. 개발자 입장에서는 자연스러운 흐름이다. 하지만 프로그램의 제어 흐름을 구현 객체가 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)이라 한다.
예를 들어,
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
위의 코드를 보면, OrderServicempl가 MemoryMemberRepository, FixDiscountPolicy란 객체를 생성하고 연결했다.
역할과 구현으로 나누었다고 하지만 OrderServiceImpl은 MemberRepository와 MemoryMemberRepository를 바라보고 있으며 또 DiscountPolicy, FixDiscountPolicy를 동시에 바라보고 있다.
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
// 생성자
public OrderServiceImpl (MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
};
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(getMemberRepository());
}
private MemberRepository getMemberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(getMemberRepository(), getDiscountPolicy());
}
private DiscountPolicy getDiscountPolicy() {
return new RateDiscountPolicy();
}
}
그렇기에 AppConfig 파일을 만듦으로써, OrderServiceImpl은 자신의 로직을 실행하는 역할만 담당하고 어떤 구현 객체들이 실행될지 알 수 없어지게 수정했다. 즉 이전의 OrderServiceImpl가 가지고 있던 제어 흐름을 이제 AppConfig가 가져간 것이다. 이렇게 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 제어의 역전(IoC)인 것이다.
2. Introduction to the Spring IoC Container
IoC의 원리는 의존성 주입(DI)*이다. 스프링 프레임워크는 빈(🫘) 생성 시에 컨테이너에 의존성을 주입한다. 이러한 과정은 Service Locator pattern** 과 같은 메커니즘을 사용하여 종속성의 인스턴스화 또는 위치를 제어하는 빈 자체의 역(즉, 제어의 반전)인 것이다.
org.springframework.beans와 org.springframework.context 패키지는 스프링 IOC 컨테이너의 기본 패키지들이다. BeanFactory 인터페이스는 객체 생성과 검색에 대한 기능을 정의하고 있다. 예를 들어 생성된 객체를 검색하는데 필요한 getBean() 메서드가 존재하며, 객체 검색 이외에도 싱글톤/프로토타입 빈인지 여부 확인하는 기능이 존재한다. ApplicationContext는 메시지 소스를 활용한 국제화 기능(한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로), 환경 변수(로컬, 개발, 운영 구분 처리), 애플리케이션 이벤트(이벤트 발행하고 구독하는 모델), 리소스조회(파일, 클래스패스, 외부)를 가지고 있는 BeanFactory의 서브인터페이스다.
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
@Test
@DisplayName("부모 타입으로 조회시, 자식이 둘 이상 있으면 중복 오류가 발생한다")
void findBeanByParentTypeDuplicate(){
DiscountPolicy bean = ac.getBean(DiscountPolicy.class);
Assertions.assertThrows(NoUniqueBeanDefinitionException.class, ()-> ac.getBean(DiscountPolicy.class));
}
@Configuration
static class TestConfig {
@Bean
public DiscountPolicy rateDiscountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public DiscountPolicy fixDiscountPolicy() {
return new FixDiscountPolicy();
}
}
3. XML-based Container Configuration and Java-based Container Configuration
xml과 @을 기반으로 한 IOC 설정방법은 다음과 같다.
요즘 많은 개발자들은 Java를 기반으로 한 스프링 설정을 많이 사용한다. XML 기반은 <bean>을 이용해 구성한다면 Java 구성에서는 일반적으로 @Configuration 클래스 내에서 @Bean을 사용한다.
* DI는 [Spring] DI로 따로 정리되어있다.
** Service Locator pattern은 디자인 패턴 중 하나로, 중개자를 통해 접근하도록 하는 패턴이다.
2023/01/26 : 왜 BeanFactory보다 ApplicationContext를 써야하는지 정리할 것
참고 1) 김영한(스프링 핵심원리 - 기본편)
참고 2) 최범균(초보 웹 개발자를 위한 스프링 4 프로그래밍 입문)
참고 3) https://docs.spring.io/spring-framework/docs/current/reference/html/core.html
Core Technologies
In the preceding scenario, using @Autowired works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking at ServiceConfig, how do
docs.spring.io