스프링 빈이 등록되는 과정(3)

스프링 빈이 등록되는 과정(3)

빈의 등록에 순서가 있을까? annotation으로 빈 설정을 하는 경우와 xml로 하는 경우에 있어 빈은 누가 먼저 등록이 되는걸까요? annotation은 componentScan한 패키지에 있는 어노테이션들을 리플랙션을 통해 가져올것이다. 어쨌든 가장 먼저 등록 되는 순서는 무엇일까요? abc순서인가? 또 xml의 경우 가장 위에 선언한 빈이 가장 먼저 등록 되는것일까요? 순서가 있다면 이런 경우는 어떻게 처리가 될까요? 요즘 제가 즐겨보는 TV프로그램중에 알쓸신잡이라고 있는데 참 쓸대없이 궁금합니다 ABean라는 클래스가 빈으로 등록되고 BBean라는 클래스도 빈으로 등록한다고 하죠, 그리고 BBean은 ABean을 inject받는다고 할때 만약 빈이 등록되는 순서가 있고 순서에 의해 BBean을 먼저 등록한다면 BBean에 inject할 ABean이 아직 등록되지 않았는데 어떻게 스프링은 처리를 할까요? [code lang=text] [/code] xml을 다음처럼 넣어놨습니다. BBean을 먼저 선언하고 그 뒤에 ABean을 선언했습니다. 과연 스프링은 BBean을 빈으로 등록하면서 ABean이 아직 등록이 안됬다고 에러를 뱉을까요? 아니면 에러없이 잘 동작할까요? 우선 빈의 등록이 당연히 정상적으로 처리되는 설정을 실행해보려고 합니다 [code lang=text] [/code] 그리고 각 빈들에 static과 생성자에 로그를 찍어놨습니다 [code lang=java] public class ABean { static { System.out.println(“ABean static init”); } public ABean() { System.out.println(“ABean init”); } } [/code] [code lang=java] public class BBean { static { System.out.println(“BBean static init”); } public BBean() { System.out.println(“BBean init”); } private ABean aBean; public void setABean(ABean aBean) { System.out.println(“aBean set”); this.aBean = aBean; } } [/code] 이렇게 실행하면 스프링 로그를 포함해서 이런식으로 실행결과가 로그로 나오게 됩니다 [code lang=text] [main] DefaultListableBeanFactory : Creating shared instance of singleton bean ‘aBean’ [main] DefaultListableBeanFactory : Creating instance of bean ‘aBean’ ABean static init ABean init [main] DefaultListableBeanFactory : Eagerly caching bean ‘aBean’ to allow for resolving potential circular references [main] DefaultListableBeanFactory : Finished creating instance of bean ‘aBean’ [main] DefaultListableBeanFactory : Creating shared instance of singleton bean ‘ahea.study.BBean#0’ [main] DefaultListableBeanFactory : Creating instance of bean ‘ahea.study.BBean#0’ BBean static init BBean init [main] DefaultListableBeanFactory : Eagerly caching bean ‘ahea.study.BBean#0’ to allow for resolving potential circular references [main] DefaultListableBeanFactory : Returning cached instance of singleton bean ‘aBean’ aBean set [main] DefaultListableBeanFactory : Finished creating instance of bean ‘ahea.study.BBean#0’ [/code] 우리가 실행시키는 BeanFactory는. DefaultListableBeanFactory인가봅니다. xml의 순서대로 aBean부터 객체를 생성하는군요. static - 생성자 순서대로 로그가 찍히게 됩니다. 그리고 Finished creating instance of bean 'aBean'이라는 로그를 찍으면서 ABean등록을 완료합니다 마찬가지로 BBean객체 생성을 진행하는데요. ABean의 진행과 같지만 property를 inject해주기 위해 singleton빈으로 이미 등록이 된 ABean을 리턴한다는 로그가 찍히는것을 볼수 있네요, 그리고 setter에 찍은 로그가 찍히네요. 로그를 자세히 보진 않았지만 대충 봐도 이정도는 알만합니다. ABean 등록 - BBean 등록 - BBean등록할 때 ABean을 inject 순으로 진행된다 이거죠~ 우리가 궁금한것은 이것입니다, 빈등록 순서를 바꾸면 위에 순서가 이렇게 바뀔텐데요 BBean 등록 - BBean등록할 때 ABean을 inject - ABean 등록 두번째 과정에서 ABean을 inject해야 하지만 아직 등록이 안됬는데? 에러가 떨어질지, 아니면 처리를 어떻게든 해줄지 궁금합니다 순서를 바꿔서 실행하겠습니다 [code lang=text] [/code] 실행결과를 보겠습니다 [code lang=text] [main] DefaultListableBeanFactory : Creating shared instance of singleton bean ‘ahea.study.BBean#0’ [main] DefaultListableBeanFactory : Creating instance of bean ‘ahea.study.BBean#0’ BBean static init BBean init [main] DefaultListableBeanFactory : Eagerly caching bean ‘ahea.study.BBean#0’ to allow for resolving potential circular references [main] DefaultListableBeanFactory : Creating shared instance of singleton bean ‘aBean’ [main] DefaultListableBeanFactory : Creating instance of bean ‘aBean’ ABean static init ABean init [main] DefaultListableBeanFactory : Eagerly caching bean ‘aBean’ to allow for resolving potential circular references [main] DefaultListableBeanFactory : Finished creating instance of bean ‘aBean’ aBean set [main] DefaultListableBeanFactory : Finished creating instance of bean ‘ahea.study.BBean#0’ [main] DefaultListableBeanFactory : Returning cached instance of singleton bean ‘aBean’ [/code] 우선 실행은 아주 잘됩니다… 우선 빈의 등록 순서를 바꾸면서 어떤 빈부터 객체생성을 하는지 보니 순서를 바꾸면서 먼저 써놓은, 즉 xml의 위에서부터 빈등록을 시작한다는 것을 알게 되었습니다(그렇군요…) 그리고 BBean이 등록이 될때 inject가 되냐 안되냐 부분은 어떻게 되는지 로그를 통해 보도록 하죠, 우선 BBean이 먼저 등록이 됩니다, static이 먼저 호출되고 생성자가 호출되는 로그를 통해 알수 있죠, 그리고 ABean을 inject해줘야 하는데 없으니 ABean을 만들기 시작합니다. 그리고 그 빈을 inject해주네요 실제 코드를 확인해봐야겠습니다. DefaultListableBeanFactory의 부모 클래스중에 AbstractAutowireCapableBeanFactory가 있습니다. Eagerly caching bean 'aBean' to allow for resolving potential circular references 라고 찍히는 로그는 여기서 찍히게 되는데요.. [code lang=java] boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName); if(earlySingletonExposure) { if(this.logger.isDebugEnabled()) { this.logger.debug(“Eagerly caching bean ‘“ + beanName + “‘ to allow for resolving potential circular references”); } this.addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return AbstractAutowireCapableBeanFactory.this.getEarlyBeanReference(beanName, mbd, bean); } }); } [/code] 로그를 찍고 나서 다음에 하는 행동이 addSingletonFactory를 통해 팩토리에 등록을 합니다. 팩토리에 빈을 등록하는 객체는 getEarlyBeanReference()을 호출하게 되는데요, getEarlyBeanReference를 레퍼런스에서 찾아보니 이렇게 설명하고 있습니다 [code lang=text] Obtain a reference for early access to the specified bean, typically for the purpose of resolving a circular reference. [/code] 영어 울렁증이 오지만 대충 읽어보면 빈의 순환구조를 맞춰주려고 하는녀석인가봅니다. 실제 메소드를 확인해보면 빈을 등록하기 위해 BeanPostProcessor가 있는것을 볼수 있습니다. 결국 순서가 빈등록의 문제를 발생시키지 않는다는 것을 알 수 있었습니다.


 Previous
스프링 빈이 등록되는 과정(4) 스프링 빈이 등록되는 과정(4)
우리는 지난 포스팅을 통해 xml, java-config 방식의 빈등록 과정을 살펴봤으며 이번시간에는 내용을 정리해보려고 합니다. 예제들은 모두 Spring Boot을 기준으로 진행되었으나 핵심적인 원리는 스프링 모
2017-06-22
Next 
스프링 빈이 등록되는 과정(2) 스프링 빈이 등록되는 과정(2)
스프링 빈이 등록되는 과정(2)1편에서는 Spring Boot을 기반으로 스프링이 빈을 어떻게 등록하는지 알아봤습니다 아주 기본적으로는 AnnotationConfigApplicationContext에 대해 알아봤는데
2017-06-22
  TOC