@Configuration을 사용한 웹 설정 파일 테스트 작성시 난감함…

이렇게 코딩을 해두고.. 이 설정 파일에서 빈으로 등록되는 것들이 제대로 등록되나 궁금하니까 테스트를 작성해볼 수 있겠죠.

이런식으로 말이죠. di() 메서드 아무것도 안해도 사실상 제가 원하는 테스트는 저정도면 충분하거든요. 그리고 이정도 테스트는 XML 기반으로 설정할 때도 잘 통과했었는데…. 흠.. 잘 안됩니다. ApplicationContext를 제대로 만들지 못합니다. 특정 빈을 만들지 못하기 때문이죠. DefaultServlet을 사용하려면 ServletContext가 필요하다는 에러가 나면서 Assert.notNull()에서 걸리고 말죠… 어찌하면 좋을까나…

스프링 TestContext를 포기할 수밖에 없는건가…

그리고 그게 꼭 필요한거면 XML 기반으로 동일한 설정을 할때도 안됐어여 하는거 아닌가..

흠.. 고민이로세.

아.. 그냥 테스트하지 말까.. 너무 당연한거니깐?? ㅋㅋㅋ

[Spring 3.1 @Enable] 2. @Import와 ImportAware

이번에는 @Import를 사용해서 빈 설정을 추가하는 방법입니다.

이렇게 @Import 애노테이션을 사용해서 다른 자바 설정을 추가할 수 있습니다. 이렇게하면, 상속을 사용하지 않아도 되지만, 상속했을 때처럼 무언가를 변경할 방법이 보이질 않습니다. 그리고 @Import에 사용할 클래스 이름을 외워야 한다는 문제는 여전히 남게되죠.

일단 @Import를 메타 애노테이션으로 사용하는 @Enable*애노테이션을 만들 수 있습니다.

이렇게 @EnableHello라는 애노테이션을 만들고, @Import로 HelloConfig.java설정을 여기에 추가해뒀습니다. 그럼 이제, AppConfig는 @Import를 직접 사용하는게 아니라, @EnableHello를 이용해서 간접적으로 사용할 수 있게 됩니다.

이제 클래스 이름을 몰라도 되긴하지만, 아직도 확장할 방법은 보이질 않죠. @EnableHello 애노테이션에 속성을 추가하고, 그 애노테이션 정보를 활용해서 빈 정보를 조작할 수 있습니다.

먼저 @EnableHello를 다음과 같이 바꿔보죠.

이렇게 애노테이션에 속성을 추가하면, 이젠 반드시 @EnableHello를 사용할 때 name 값을 줘야 합니다. (물론, default 키워드로 기본값을 설정하면 강제적으로 설정하지 않게 할 수도 있죠.)

그럼 그리고 이제 HelloConfig가 ImportAware라는 인터페이스를 구현하게합니다.

이렇게 ImportAware 인터페이스의 구현 메서드에서 @Import가 붙어있는 곳(여기서는 AppConfig.java)의 애노테이션 정보(@EnableHello)를 바탕으로 빈 속성을 변경하게 했습니다.

이게 끝이아니라, 이제부터 시작입니다.

만약에 지금처럼 단순하게, 빈의 내용만 바꾸는게 아니라, 특정 속성에 따라 등록되어야 하는 빈 자체가 달라진다면 어떻게 해야할까요? 지금은 HelloConfig에 선언한 모든 @Bean이 반드시 빈으로 등록되고 있는데, 특정 옵션에 따라 어떤 빈이 필요하기도 하고, 필요없기도 하다면… ㅎㅎ 문제가 점점 재밌어 집니다.

[Spring 3.1 @Enable] 1. @Configuration 상속을 사용한 확장 방법

저는 사실 오늘 토비님 발표를 두번봤습니다. 실제 발표 시간 때 한번봤고, 발표하기 전에 리허설 때 한번 먼저 봤습니다. 리허설을 보기 전에는 발표 자료를 또 먼저 봤었구요. 발표자료만 봤을 때는 꽤나 어렵게 느껴졌는데, 리허설 때 라이브 코딩을 보니까 이해하기가 더 수월했습니다. 그리고 실제 발표 시간에는 직접 토비님 코딩에 맞춰서 저도 똑같이 따라서 코딩했습니다.

어쩄든 보면서 코딩했던걸 되새기며, 까먹기 전에 후딱 기록합니다.

먼저, 별로 권장할만한 방법은 아니지만, 가장 단순한 방법은 @Configuration 클래스를 상속받는 방법입니다. 상속 받아서 오버라이딩을 하거나, 새로운 @Bean을 추가할 수 있겠습니다.

이런 클래스가 있고, 이걸 자바 설정으로 다음과 같은 빈 설정 파일을 만들었다고 가정하겠습니다.

이 빈 설정에 등록한 Hello @Bean을 다른 빈 설정에서도 재사용 및 확장하고 싶다면, 다음과 같이 단순하게 상속해서 오버라이딩 할 수 있습니다.

하지만, 다음과 같은 문제가 있죠.

  • 클래스 이름 외워야 한다.
  • 자바는 단일 상속이기 때문에 다른 설정은 확장하지 못한다.
  • 자바의 상속이 가진 모든 단점도 지니게 된다.
  • @Configuration의 @Bean은 CGLib 프록시를 사용하기 때문에 @Bean 메서드에 final을 사용해서 중요 로직이 오버라이딩되지 않도록 방지할 수가 없어서, 자칫 오버라이딩으로 인해 중요 로직까지 바뀔 수 있다.

그래서 이 방법은 가장 단순한 방법이긴 하지만, 별로 권장할만한 방법은 아닙니다. 그렇다고 쓰지 말라는 법은 없으니깐 뭐.. 흠냐..

[스프링 3.0] 스프링 bean과 일반 자바 객체가 호출하는 @Bean 메서드의 차이

public class JavaConfigTest {

    AnnotationConfigApplicationContext ac;

    @Before
    public void setUp(){
        ac = new AnnotationConfigApplicationContext(AppConfig.class);
        assertThat(ac, is(notNullValue()));
    }

    @Test
    public void getBean(){
        SampleBean bean1 = ac.getBean(“sampleBean”, SampleBean.class);
        SampleBean bean2 = ac.getBean(“sampleBean”, SampleBean.class);
        assertThat(bean1, is(notNullValue()));
        assertThat(bean2, is(notNullValue()));
        assertThat(bean1, is(bean2));

        AppConfig config1 = new AppConfig();
        SampleBean bean3 = config1.sampleBean();
        assertThat(bean3, is(not(bean2)));

        AppConfig config2 = ac.getBean(“appConfig”, AppConfig.class);
        SampleBean bean4 = config2.sampleBean();
        assertThat(bean4, is(bean2));
    }

}

스프링 3.0 애노테이션 기반 설정을 익히기 위해서 처음 만들어본 테스트입니다. 이 테스트에서 알 수 있는 건 바로 이 글의 제목에서처럼 @Bean이 붙어있는 메서드를 어떤 객체를 이용해서 호출하느냐에 따라 그 결과가 다를 수 있다는 겁니다.

@Configuration
public class AppConfig {

    @Bean
    public SampleBean sampleBean(){
        return new SampleBean();
    }
}

이건 설정한 빈이고 SampleBean은 뭐 암거나;; @_@;

결론은 new 로 만든 AppConfig와 스프링에서 가져온 AppConfig의 @Bean이 붙은 메서드가 반환해주는 값이다르다는 겁니다. 전자는 일반적인 Java 문맥대로 sampleBean()에서 new SampleBean()으로 새로운 객체를 만들어서 받은 것이고, 후자는 sampleBean() 메서드 호출을 가로채서 기존의 bean을 반환해준 겁니다.

이 경우는 매우 간단한 경우에 속합니다. 하나는 스프링이 관리하는 빈이었고, 하나는 일반 자바 객체였으니까요. 그런데 만약에 둘 다 스프링의 빈이라면? 그 중에 하나는 @Configuration, 다른 하나는 @Component라면?

@Component로 설정한 빈 내부에 위와 똑같은 설정이 들어있다면? 반환하는 객체의 값이 조금 다르나면??

빈 스캔은 어찌하나?

정말 복잡하군요;; 하나씩 차근 차근 해보겠습니다.