Spring bean dependency resolution, 스프링 Bean의 의존성과 생성 / 파괴 순서

Spring 앱이 죽을 때, 나쁜 순서로 bean을 destroy하는 과정에서 불필요한 에러가 발생할 수 있다.

Bean Destruction Order

Bean은 생성 순서의 역순으로 destroy 된다고 한다.

Bean Initialization Order

각종 Config가 import 되고 정의된 순서대로 bean을 생성한다. bean을 생성하는 도중에 의존성을 발견하면, 생성 중인 bean이 의존하는 bean을 먼저 생성해 주입한다. Bean A가 bean B에 의존하고 있다면 B가 A보다 먼저 생긴다.

Beans Dependency Resolution

Spring이 의존성을 파악하고 주입하는 단계는 다음과 같다.(https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-dependency-resolution)

  1. The ApplicationContext is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified via XML, Java code or annotations.
  2. For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method if you are using that instead of a normal constructor. These dependencies are provided to the bean, when the bean is actually created.
  3. Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.
  4. Each property or constructor argument which is a value is converted from its specified format to the actual type of that property or constructor argument. By default Spring can convert a value supplied in string format to all built-in types, such as intlongStringboolean, etc.

단순히 bean A를 다른 bean B의 property로 넣는 것은, Spring 입장에서 dependency가 아니다. Spring이dependency로 인식해 생성 순서를 조절하는 경우는 다음과 같다.

  1. Spring은 xml 상에서 </bean> 혹은 Java config 상에서 @Bean method 내부에서 constructor argument로 넣어주거나, setter를 호출한다.
  2. @Autowired 등을 이용해 Annotation based DI를 해준다.
  3. @DependsOn을 이용해 의존관계를 명시해준다.

Cases

  1. A라는 aspect를 bean으로 생성해서 여러 함수에 적용했다. 이 때 Spring은 A와 A가 aspect로 적용된 bean들이 dependency가 있다고 판단하지 않는다. 따라서 A는 먼저 죽을 수 있다. 죽지 않은 다른 bean들의 함수가 호출 되며 A의 함수가 advice로 호출된다. A가 먼저 죽었다면, 에러가 발생한다.
  2. 스프링에 의해 scheduling되는  task를 Runnable type의 Bean B로 생성한다. 이 때 run() 함수 안에서 다른 bean C의 함수를 closure를 이용해 호출한다. 이 때 C는 B의 property나 constructor argument로 들어가지 않았기 때문에 명시적 의존성이 생기지 않는다.  따라서 앱이 죽을 때, C는 먼저 죽을수 있다.  B는 스프링에 의해 scheduledTask로 계속 실행된다. B가 C를 사용하려다 에러가 발생한다.
  3. D라는 bean을 생성한다. E라는 bean을 생성한다. @PostConsturct를 통해 E의 listener list에 D를 추가한다. E라는 bean을 생성할 때 setter를 호출해 설정한 listener가 아니므로, 역시 의존성으로 간주되지 않는다. 따라서 앱이 죽을 때, D가 먼저 죽을 수 있다. E는 listener인 D를 호출하다가 에러를 발생 시킨다.

Solutions

  1. @DependsOn으로 의존성을 명시에 빈이 생성되고 파괴되는 순서를 결정했다.
  2. Spring 4.3 부터는 context.stop()을 호출하면, scheduled task 들을 먼저 정리해준다. 기존에 4.2 버전을 사용하고 있어, 버전을 올리고 shutdown listener에서 context.stop()을 호출했다.
  3. 1과 동일.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google photo

Google의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중

search previous next tag category expand menu location phone mail time cart zoom edit close