PropertyPlaceholderConfigurer는 프로퍼티 파일의 속성을 가지고 ${database.name}과 같은 프로퍼티 값을 바꿔주는 매우 유용한 기능을 가지고 있어서 자주 사용된다. 스프링의 기초정도만 배웠어도 DataSource를 직접 스프링에서 정의하는 할 때 DB연결정보는 프로퍼티 파일로 빼고 ${database.url}과 같은 식으로 쓰라는 것은 한번쯤 들어봤을 것이다.

context 네임스페이스를 쓰면 더욱 정의하기 편하다. 다음과 같이 적어주면 내부적으로 PropertyPlaceholderConfigurer가 만들어지고 모든 ${}로 정의된 빈의 프로퍼티 값을 프로퍼티 파일의 바꿔치기 해준다.

 <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>

 

프로퍼티 값은 애노테이션을 쓴다면 @Value에 의해서 지정할 수 있다.

그렇다면 다음과 같은 코드도 만들어 볼 수 있지 않을까?

public class BeanSP {

    @Value("#{systemProperties['os.name']}")

    String name;

    @Value("${database.username}")

    String username;

    @Bean

    public PropertyPlaceholderConfigurer databaseProperty() {

        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();

        ppc.setLocation(new ClassPathResource("database.properties", getClass()));

        return ppc;

    }

}

name은 SpEL을 사용해서 다이나믹하게 시스템 프로퍼티 값을 가져온 것이다. SpEL이 알아서 동작하니 신경쓸 건 없고.

두번째 username은 PropertyPlaceholderConfigurer에 의해서 변경되는 전형적인 ${}을 가지고 있으니 적절한 PropertyPlaceholderConfigurer와 프로퍼티 파일만 만들어주면 DB접속이름으로 변경될 것이라고 기대한다.

보통 PropertyPlaceholderConfigurer은 XML에 정의하지만 XML없는 컨텍스트를 사용하기 위해서 @Bean을 붙여 자바코드에 의한 빈 설정 기능을 사용했다. 이제 BeanSP만 빈으로 등록해주면 @Bean이 붙은 databaseProperty() 메소드에 의해서 PropertyPlaceholderConfigurer 타입의 빈이 등록될 것이고 그러면 당연히 username의 값도 변경될 것이라고 기대할 수 있다.

 

그러나 테스트 해보면 ${database.username}은 바뀌지 않는다. 파일도 잘 만들었을 뿐더러 확인해보면PropertyPlaceholderConfigurer 타입의 빈도 정상적으로 등록되어있다. XML로 바꿔서 등록하면 당연히 잘 된다.

그렇다면 @Bean을 사용해서 정의하면 왜 안될까?

 

이를 알려면 PropertyPlaceholderConfigurer @Bean애노테이션이 붙은 클래스를 스프링이 어떻게 처리하는지에 대한 두가지 지식이 모두 필요하다.

이 두가지 기능은 모두 BeanFactoryPostProcessor(BFPP)에 의해서 처리된다. BFPP는 빈 정의가 모두 준비되었을 때 진행하는 빈 팩토리 후처리기이다. 준비된 빈 정의 자체를 어떤 식으로든 바꿔치기 할 수 있기 때문에 상당히 유용하다.

PropertyPlaceholderConfigurer는 프로퍼티 파일의 속성과 빈 정의에 나온 프로퍼티 값을 비교해서 일치하면 프로퍼티 값 정의 자체를 바꿔주는 것이다. 그래서 ${database.name}이 user1과 같은 값 설정으로 바뀌고 나중에 빈을 만들면 이 빈 정의에 따라서 바뀐 값으로 만들어지는 것이다.

@Bean에 의해서 빈이 등록되는 것은 ConfigurationClassPostProcessor(CCPP) BFPP에 의해서 진행된다. 이 BFPP는 한 술 더 떠서 아예 새로운 빈 정의를 추가해준다. BFPP는 빈 정의를 어떤 식으로든 조작할 수 있는 강력한 기능을 가지도록 정의된 것으로 스프링은 이를 이용해서 많은 기능을 확장해왔다. 그래서 이 BFPP가 동작하는 동안에 @Bean이 붙은 빈 정의를 발견하면 그 내용을 토대로 새로운 빈 정의를 추가해준다.

자.. 문제는 BFPP가 실행되는 시점이다.  순서를 잘 보자.

1. 먼저 위의 BeanSP가 빈으로 등록된다. XML이든 스캐닝이든, 직접 주입이든 암튼 컨텍스트의 빈 설정으로 등록된다. 이 때 아직 @Bean에 의해서 CCPP가 만들어지지 않았다.

2. 이미 ConfigurationClassPostProcessor는 등록되어있다. AnnotationConfigApplicationContext라면 초기화 과정에서 추가해줄 것이고, XML이라면 <context:annotation-config>에 의해서 추가되어있을 것이다.

3. 기본 빈 등록이 끝났으므로 등록된 빈 중에서 BFPP를 찾는다. 지금까지 빈으로 등록된 BFPP는 ConfigurationClassPostProcessor뿐이다.

4. 모든 BFPP를 실행한다. 따라서 CCPP가 후처리기로 동작한다. 이 때 @Bean 메소드를 발견하고 PropertyPlaceholderConfigurer 타입의 빈을 등록해준다.

5. 끝.

문제는 3과 4의 순서이다. @Bean에 의해서 등록되는 빈은 이미 3번 과정에서 BFPP를 모두 찾아서 BFPP에 대한 실행이 시작된 이후에 일어난다. 따라서 4번 과정에서 등록된 PropertyPlaceholderConfigurer은 다시 3번으로 돌아가서 컨텍스트가 실행할 BFPP의 후보로 추가될 수 없다.

따라서 PropertyPlaceholderConfigurer는 빈으로 등록되지만 BFPP로 실행되는 시점이 지난 후기 때문에 무용지물이다.

 

결론은 "BFPP 빈은 다른 BFPP(CCPP)에서 등록되게 만들면 안된다".

XML을 사용하지 않으면서 위의 문제를 풀려면 다음과 같이 아예 독립적으로 PropertyPlaceholderConfigurer를 만들어 초기부터 빈으로 바로 등록되게 하면 된다.

@Component

public class DatabasePropertyPlaceHolder extends PropertyPlaceholderConfigurer {

    public DatabasePropertyPlaceHolder() {

        this.setLocation(new ClassPathResource("database.properties", getClass()));

    }

}

비슷한 원리가 BeanPostProcessor에도 적용될 수 있다. BPP에서 빈을 등록할 일은 없겠지만 기존 프로퍼티 내용을 조작하려고 하려면 BPP의 순서를 잘 염두에 둬야 한다.

또, BFPP가 단지 시간순서의 문제라면 BFPP빈의 실행순서를 조정하는 것은 가능하다. priority나 order를 지정하면 우선적으로 실행될 BFPP를 지정할 수 있다.

AC가 빈 등록이후 초기화 하는 작업의 순서를 알고 싶다면 AbstractApplicationContext의 refresh() 메소드를 참고하면 된다. 구체적인 후처리 작업에 대해서는 BFPP와 BPP를 beanFactory에서 가져와 출력해보면 된다. 스프링 초보자가 아니라면 적어도 자신이 만든 설정에 의해서 어떤 BF, Bean 후처리기들이 동작하는지쯤은 알아야 한다.

Related posts:

  1. Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기
  2. Spring 3.0 (59) 프로퍼티 파일 이용하기 – placeholder vs SpEL
  3. Spring 3.0 (60) 클래스패스 리소스를 지정할 때 주의사항과 팁
  4. 스프링 3.1 (4) Static @Bean 메소드
  5. Spring 3.0 (14) Context Support 모듈의 선택 라이브러리 분석
  6. Spring 3.0 (26) Spring Expression Language와 @Value
  7. Spring 3.0 (33) JavaConfig의 통합과 변신. 메타-빈(meta-bean) 개념의 등장.
  8. Spring 3.0 (13) Context 모듈의 선택 라이브러리 분석
  9. Spring 3.0 (15) Jdbc 모듈의 선택 라이브러리 분석
  10. Spring 3.0 EL (Spel)을 이용한 AssertThrows 확장 (1)
  11. Spring 3.0 (56) @Bean 사용의 주의사항
  12. Spring 3.0 (58) Static Class를 XML없이 빈으로 등록하기
  13. Spring 3.0 (20) Transaction 모듈의 선택 라이브러리
  14. Spring 상식퀴즈 (1) – DI 태클하기
  15. Spring 3.0 (2) R-518 스프링의 새 모듈 OXM(Object/XML Mapping)

Facebook comments:

to “Inside Spring (5) PropertyPlaceholderConfigurer를 @Bean으로 정의해서는 안되는 이유”

  1. 흠냐.. SpEL이 원하던대로 동작하지 않았을 땐 그냥 ${database.username} 이 값이 들어가 버리는군요.

    테스트 해봤더니 뭔가 좀 불편하네요. JavaConfig에 @PropertiesValueSource 이런게 있어서 간단하게 애노테이션만 가지고 프로퍼티 파일을 설정해 추가할 수 있었는데 그건 아직 지원 안하나보죠?

  2. 바쁘실텐데, 이런 도움되는 글을…
    아무래도 Toby님 책은 스프링에 대해 굉장히 자세하게 설명되어 있을것이라 예상이 됩니다.

  3. JeOVF5 mlghgfmylvoi, [url=http://qdweatibreth.com/]qdweatibreth[/url], [link=http://lzsqfsiobgel.com/]lzsqfsiobgel[/link], http://wevkkgligauo.com/

  4. Everything is very open with a very clear explanation
    of the issues. It was truly informative.
    Your website is useful. Thanks for sharing!

  5. Υou are so cоol! I do not bеlieve I’ve read through a single thing like that before. So wonderful to find somebody with a few genuine thoughts on this issue. Seriously.. thank you for starting this up. This web site is one thing that is needed on the internet, someone with a bit of originality!

    my web blog … buy Cialis

  6. That’s Too good, when it comes in india desire may well make a Rocking position for youngster.. desire that come true.

  7. You should try this offer for getting more traffic: http://gmbal.com/210c – I use it on all of my sites and I am very happy. This service will get you targeted website visitors with no effort on your part. Thank me later!

  8. mbt antishoes Inside Spring (5) PropertyPlaceholderConfigurer를 @Bean으로 정의해서는 안되는 이유 » Toby’s Epril

  9. mbt running shoes Inside Spring (5) PropertyPlaceholderConfigurer를 @Bean으로 정의해서는 안되는 이유 » Toby’s Epril

  10. Njrjd842ufshbcs

  11. If you get up one more time than you fall you will make it through.

Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2017 Toby's Epril Suffusion theme by Sayontan Sinha