IoC 또는 DI의 기본 개념은 "제어권이 역전되었다" 는 뜻이다.
일반적으로 의존성을 해결하는 방법으로는 자기가 사용할 의존성을 자기 자신이 직접 만들어서 사용하는 것이다. 즉, 의존성에 대한 제어권을 자기 자신이 가지고 있다.
class OwnerController {
private OwnerRepository repository = new OwnerRepository();
}
그런데 의존성에 대한 제어권이 역전되었다는 것은 의존성을 가지고는 있지만 자기 자신이 직접 만들지는 않는다는 것이다. 따라서 의존성에 대한 제어권은 자기 자신이 아니라 외부에 있다. 따라서 제어권이 역전되었다고 말할 수 있는 것이다.
class OwnerController {
private OwnerRepository repo;
public OwnerController(OwnerRepository repo) {
this.repo = repo;
}
// repo를 사용합니다.
}
위의 예시에서는 OwnerController 밖에서 누군가가 의존성을 주입한다. 이 예시에서는 생성자를 통해 의존성을 받아온다. 따라서 DI(Dependency Inversion) 또한 IoC(Inversion Of Control)의 일종이라고 볼 수 있는 것이다.
프레임워크는 제어의 역전 개념이 적용된 대표적인 기술이다. 프레임워크는 단지 미리 만들어둔 반제품이나 확장해서 사용할 수있도록 준비된 추상 라이브러리의 집합이 아니다. 라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어한다.
반면에 프레임워크는 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다. 보통 프레임워크 위에 개발된 클래스를 등록해두고, 프레임워크가 흐름을 주도하는 중에 개발자가 만들 애플리케이션 코드를 사용하도록 만드는 방식이다.
스프링 IoC 컨테이너가 하는 주요한 기능은 "빈(bean)을 만들고 엮어주며 제공해주는 것"(빈의 생성, 빈의 의존성 주입, 빈의 스코프 관리)이다. 여기서, 빈(bean)
이란 IoC 컨테이너가 관리하는 객체
를 일컫는다.
스프링 컨테이너에서 모든 객체를 관리해주는 것은 아니며, 다음과 같은 방법에 의해 빈을 등록할 수 있다.
또한 빈들은 다음과 같은 설정 값들을 가지고 있다.
빈 설정
또, 등록한 빈을 꺼내쓰는 방법(의존성을 주입받아서 사용)은 다음과 같다.
그렇다면 필요한 의존성을 어떻게 받아올 것인가? 즉, @Autowired / @Inject를 어디에 붙일까?
이 중 스프링 프레임워크 레퍼런스가 권장하는 방법은 "생성자"이다. 그 이유는 필수적으로 사용해야 하는 레퍼런스(매개변수) 없이는 인스턴스를 만들지 못하도록 강제할 수 있기 때문이다. 나머지 필드 Injection이나 Setter Injection은 이러한 의존성 없이도 일단 인스턴스를 만들 수 있다. 그러나 A가 B를 참조하고, B가 A를 참조하는 순환참조의 경우에는 생성자 Injection으로 인스턴스를 만들 수 없기 때문에, 일단 인스턴스를 만들고 의존성을 주입해야 하므로 필드 Injection이나 Setter Injection을 사용해야 한다.
또한, 스프링 IoC 컨테이너는 오직 "빈"들만 의존성 주입을 해준다.
ApplicationContext
는 애플리케이션에 구성 정보
를 제공하기위한 스프링 애플리케이션 내의 중앙 인터페이스이다.
다음은 스프링 공식 문서에 나와있는 ApplicationContext가 제공하는 기능이다.
특히 ApplicationContext가 제공하는 기능 중 1번을 보면 BeanFactory 메소드가 있다. 즉, ApplicationContext는 BeanFactory를 상속받은 것이다.
BeanFactory
는 애플리케이션 컴포넌트의 중앙 저장소
이며, 빈 설정 소스로 부터 빈 정의를 읽어들이고, 빈을 구성하고 제공하는 기능을 한다.
스프링 공식 문서를 살펴보면 BeanFactory의 역할을 자세히 확인할 수 있다.
Spring Bean 컨테이너에 액세스 하기 위한 루트 인터페이스이다.
이 인터페이스는 각각 문자열 이름으로 고유하게 식별되는 여러 Bean 정의를 보유하는 오브젝트에 의해 구현된다. 빈 정의에 따라 팩토리는 포함된 객체의 독립 인스턴스 (Prototype 디자인 패턴) 또는 단일 공유 인스턴스 (인스턴스가 범위의 싱글톤인 Singleton 디자인 패턴의 우수한 대안)를 반환한다.
리턴되는 인스턴스 유형은 Bean 팩토리 구성에 따라 다르다. Spring 2.0부터 구체적인 애플리케이션 컨텍스트 (예 : 웹 환경에서 "요청"및 "세션"범위)에 따라 추가 범위를 사용할 수 있다.
일반적으로 BeanFactory 조회와 같은 "pull" 구성 형식을 사용하는 것보다 setter 또는 생성자를 통해 응용 프로그램 개체를 구성하려면 종속성 주입 ("push" 구성)에 의존하는 것이 좋다. Spring의 Dependency Injection 기능은 이 BeanFactory 인터페이스와 하위 인터페이스를 사용하여 구현된다.
이 접근 방식의 요점은 BeanFactory가 애플리케이션 구성 요소의 중앙 레지스트리
이며 애플리케이션 구성 요소의 구성을 중앙 집중화한다는 것이다(예를 들어 개별 개체가 더 이상 속성 파일을 읽을 필요가 없음).
일반적으로 BeanFactory는 구성 소스 (예 : XML 문서)에 저장된 Bean 정의를 로드하고 org.springframework.beans 패키지를 사용하여 Bean을 구성합니다. 그러나 구현은 필요에 따라 Java 코드에서 직접 생성하는 Java 객체를 간단히 반환할 수 있다. LDAP, RDBMS, XML, 속성 파일 등과 같이 정의를 저장하는 방법에는 제약이 없다. 구현은 Bean 간의 참조를 지원하도록 권장된다(종속성 주입).
빈 팩토리 구현은 가능한 한 표준 빈 라이프 사이클 인터페이스
를 지원해야한다. 전체 초기화 방법 및 표준 순서는 다음과 같다.
빈 팩토리 라이프사이클 인터페이스와 메소드
Bean 팩토리 종료시 다음 라이프 사이클 메소드가 적용된다.
참고