InsideSpring (1) Annotated Factory Method (@Configuration)을 쓰는 4가지 방법 (3)
네번째 방법을 알아보자.
여기서 첫번째 XML을 이용한 annotation-config이 어떻게 @Configuration 클래스의 처리를 해주는지를 확인할 수 있다.
스프링2.0부터 도입된 XML 네임스페이스를 이용한 설정방법은 기존에 원시적인 오브젝트를 그대로 설정해줘야 하는 <bean> 뿐인 XML에 큰 변화를 주었다. 애플리케이션 컴포넌트가 아닌 컨테이너 설정을 위한 transaction, aop 같은 설정용 빈의 등록이나, JNDI와 같은 기술서비스의 등록은 보다 의미가 명확하게 노출되는 별도의 네임스페이스를 가지는 태그로 정의할 수 있게 되었다. XML이 줄어든 것도 가치가 있지만 그보다는 의도가 명확하게 드러난다는 점이 더 중요한 것이다.
이렇게 네임스페이스를 가지는 태그를 등록하면 스프링은 그 네임스페이스가 정의된 스키마를 찾아서 그 핸들러를 확인하고 그것을 호출한다. context 네임스페이스의 핸들러를 찾으려면 META-INF 밑의 spring.handlers 파일을 열어보면 된다.
이런 내용을 찾을 수 있다.
http\://www.springframework.org/schema/context= org.springframework.context.config.ContextNamespaceHandler
그래서 처음 XML에서 썼던
<context:annotation-config />
를 스프링이 보고 위의 ContextNamespaceHandler를 불러주는 것이다. 태그에 정의된 애트리뷰트나 차일드 정보를 같이 넘져준다.
보통 핸들러들이 하는 일은 대부분 빈의 등록이다. 그냥 빈으로 등록해도 되는 것을 편하고 의미있게 태그로 만들어준 것이다. 더 나가서 태그 하나에 여러개의 빈을 등록하기도 한다.
ContextNamespaceHandler를 살펴보면 태그별로 다시 BeanDefinitionParser가 등록되어있어서 해당 tag의 처리를 담당하는 쪽으로 넘기게 된다. annotation-config은 AnnotationConfigBeanDefinitionParser여기서 처리. 이 코드를 보면 먼저 유틸 메소드를 호출해 빈의 정의를 받아온다. 그리고 이걸 컴포넌트로 구성해서 등록한다. 중첩 태그로 구성되어있을 경우 등등을 고려한 처리등이 있다.
중요한 것은 이 태그에 의해서 정의되는 빈들이 있다는 것이다.
// Obtain bean definitions for all relevant BeanPostProcessors.
Set<BeanDefinitionHolder> processorDefinitions =
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
이 코드를 보면 AnnotationConfigUtils.registerAnnotationConfigProcessors를 불러서 그 등록작업을 처리하게 한다.
그 안을 보면 기본적으로 4개의 빈을 등록하게 되어있다. JPA용도 있는데 이건 특별하니까 일단 무시.
4개를 살펴보면 이렇다.
- ConfigurationClassPostProcessor
- AutowiredAnnotationBeanPostProcessor
- RequiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
즉 annotation-config 태그 하나를 보고 위의 4개의 PostProcessor 빈을 등록해주는 것이다.
바로 이중의 첫번째가 우리가 직접 써봤던 바로 그 ConfigurationClassPostProcessor이다. 그래서 결국 annotation-config만 해줘도 ConfigurationClassPostProcessor가 등록이 되기 때문에 애플리케이션 컨텍스트에서 자동으로 저 BFPP를 적용해주게 되는 것이다.
보너스로 나머지도 보면 @Autowired를 담당하는 AutowiredAnnotationBeanPostProcessor, @Required를 담당하는 RequiredAnnotationBeanPostProcessor, 그리고 JSR-250 표준애노테이션을 담당하는 CommonAnnotationBeanPostProcessor가 각각 등록이 되서 적용되는 것이다.
기존에 2.5에서는 이 세가지만 있었지만 3.0에서 ConfigurationClassPostProcessor 추가 된 것이라고 보면 된다.
그렇다면 만약 @Autowired 같은 것은 필요없고 나는 자바클래스 설정인 @Configuration만 쓰고 싶은데 XML로 정의하고 싶다면 어쩔까? 그럼 그냥 직접 저 PostProcessor를 등록해주면 된다.
<context:annotation-config />
을 빼고
<bean class="org.springframework.context.annotation.ConfigurationClassPostProcessor" />
만 넣어도 된다는 것이다. 스프링의 모든 네임스페이스를 쓴 태그들은 결국 직접적인 빈의 등록으로 다 대치할 수 있다. 그 안에서 이상한 짓만 하지 않는다면.
이제 왜 XML의 설정으로 @Configuration처리가 됐는지 다 알아봤다.
내가 쓴 4번째 방법은 이 handler 내부에서 썼던 AnnotationConfigUtils.registerAnnotationConfigProcessors 유틸리티 메소드를 사용해본 것이다. 이 등록부분을 구지 Utils에 따로 둔 이유는, 다른 곳에서도 사용하기 때문이다. 테스트에서 특히 많이 사용하고.
그럼 이걸 호출해서 PostProcessor를 등록해도 된다는 것.
GenericApplicationContext ctx = new GenericApplicationContext();
ctx.registerBeanDefinition("config", new RootBeanDefinition(Config.class));
AC만드는 것은 똑같고 대신 직접 PP를 등록 안하고 위의 메소드를 이용한다.
AnnotationConfigUtils.registerAnnotationConfigProcessors(ctx, null);
그리고 AC refresh() 해주면 끝.
ctx.refresh();
String hello = ctx.getBean("hello", String.class);
assertThat(hello, is("Hello"));
분석 끝.
@Configuration의 XML을 이용한 사용방법 하나만 그 동작원리를 찾아보려고 하면 이만큼 많은 것을 공부할 수 있다. 이런게 스프링을 연구하는 재미가 아닐까.
이거 하나만 제대로 연구해도 이런 보너스가.
- BeanFactoryPostProcessor 은 무슨 짓을 하는가.
- BeanDefinition로 빈이 등록되는 방법.
- BeanFactory + ApplicationContext의 관계와 인터페이스와 클래스 계층구조.
- Namespace/Handler 동작원리.
좋은글 잘 읽었습니다. 왜루가 설정이 약간 다를까 했는데, 좋은 설명이 되었습니다.^^*