본문 바로가기

SpringFramework Core - I. IoC 컨테이너/4. 의존성

4.1.3. 의존성 주입 프로세스

원문: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-dependency-resolution

 

 

컨테이너는 다음과 같이 의존성을 주입 작업을 한다.

  • ApplicationContext가 모든 bean을 설명하고 있는 설정 메타데이터를 통해 생성되고 초기화된다. 설정 메타데이터는 XML, 자바 코드, 어노테이션을 통해 특정된다.
  • 각각의 bean에 대해, 그들의 의존성은 properties, 생성자 매개변수, static 팩토리 메서드의 인자 형태로 표현된다. 이러한 의존성들은 bean이 실제로 생성될 때 bean에게 제공된다.
  • 각각의 프로퍼티 또는 생성자 매개변수는 세팅될 값에 대한 실질적인 정의이거나 컨테이너 내의 다른 bean에 대한 참조이다.
  • 각각의 프로퍼티 또는 생성자 매개변수는 특정한 형식에서 실제 프로퍼티/생성자 매개변수의 타입으로 변환된다. 기본적으로 스프링은 문자열로 제공받은 값들을 int, long, String, boolean 등의 built-in 타입들로 변환할 수 있다.

 

스프링 컨테이너는 컨테이너가 생성됨에 따라 bean들이 가진 설정의 유효성을 검사한다. 하지만 bean의 프로퍼티는 bean이 실제로 생성될때까지 세팅되지 않는다. 컨테이너가 생성될 때, 싱글턴 scope의 bean들과 pre-instantiated되어야하는 것들이 생성된다. Scope들에 대해서는 Bean Scopes에 설명되어 있다. 컨테이너가 생성될 때 생성되지 않은 bean들은 요청받았을 때만 생성된다. bean의 생성은 잠재적으로 일련의 bean 생성을 야기한다. 해당 bean의 의존성과 그 의존성의 의존성이 생성되고 할당되어야하기 때문이다. 그러한 의존성 작업 과정에서의 불일치는, 처음으로 영향을 받는 bean이 생성될 때에서야 드러날 것이다.

 

 순환 의존성
생성자 주입 방식을 주로 사용한다면, 해결할 수 없는 순환 의존성의 상황을 만들어 낼 수 있다.

예를 들어, 클래스A는 클래스B의 인스턴스를 생성자 주입을 통해 필요로 한다. 그리고 클래스B는 클래스A의 인스턴스를 생성자 주입 방식을 통해 필요로 한다. 만약 클래스A와 클래스B의 bean을 서로 필요하도록 설정한다면 스프링 IoC 컨테이너는 이러한 순환 참조를 런타임시에 감지할 것이다. 그리고 BeanCurrentlyInCreationException 에러를 발생시킬 것이다.

한가지 해결방법은 몇몇 클래스의 소스코드를 생성자 방식에서 setter 방식으로 바꾸어주는 것이다. 또다른 방법으로는 아예 생성자 주입방식을 사용하지 않고 setter 주입방식 한 가지만 사용하는 것이다. 즉, 권장되지는 않지만 setter 주입을 통해 순환 의존을 해결할 수 있다.

bean A와 bean B 사이의 순환 의존에서는, 자신이 완전히 초기화되기도 전에 다른 bean에게 주입되도록 강요받는다. 마치 닭이 먼저냐 달걀이 먼저냐하는 문제와 같이 말이다.

 

보통은 스프링이 바람직한 방향으로 작업하는 것을 신뢰하면 된다. 스프링은 컨테이너가 로딩될 때, 존재하지 않는 bean을 참조하는 문제라든가 순환 의존같은 설정 문제들을 감지해낸다. 스프링은 bean이 실제로 생성될 때, 가능한 한 늦게 프로퍼티들을 세팅하고 의존성들을 주입한다. 이 말은 올바르게 로딩을 수행하는 스프링 컨테이너는, 객체나 의존성 생성에 문제가 있는 객체가 요청을 받을 때 발생할 수 있는 예외를 나중으로 미룬다는 것을 의미한다. 예를 들어, 누락되거나 유효하지 않는 프로퍼티 결과로 인해 예외를 던지는 bean이 있다고 하자. 기본적인 ApplicationContext 구현체들이 싱글턴 bean을 사전 인스턴스화하는 이유는 몇몇 설정 문제들에 대한 가시성을 잠재적으로 지연시키기 위해서이다. bean들이 실제로 필요하기 이전에 미리 시간과 메모리를 소모해가며 bean들을 생성함으로써, 나중이 아닌 ApplicationContext가 생성되는 시점에서 설정 문제들을 발견할 수 있다. 이런 기본적인 작동 방식을 오버라이드해서 싱글턴 bean들이 사전 인스턴스화되는 대신에, 늦게 초기화되도록 할 수 있다.

 

만약 순환 의존이 존재하지 않는다면, 각각의 협력하는 bean들은 독립적인 bean에 대해 주입되기 전에 완전히 설정될 것이다. 이는 만약 bean A가 bean B를 의존한다면, 스프링 IoC 컨테이너는 bean A의 setter 메서드를 호출하기 전에 bean B를 완전히 설정할 것이라는 의미가 된다. 다른 말로 하면, 만약 bean이 사전 인스턴스화되는 싱글턴이 아니라면, bean이 인스턴스화된 후 의존성이 세팅되며 그리고 나서 관련된 라이프사이클 메소드들이 호출될 것이라는 것이다. (설정된 초기화 메서드bean 초기화 콜백 메서드 등등)