[Spring 3.1 @Enable] 3. @Import와 ImportSelector

이번에는 새로운 @Configuration을 하나 추가해보죠. 예를 들어, @EnableHello의 type이라는 속성값이 “korean”일때는 HelloKoreanConfig라는 @Configuration을 사용하고, type이 “english”일때는 HelloConfig를 사용하도록 하는거죠.

그리고 ImportSelector 인터페이스 구현체를 만들어서, 특정 애노테이션 속성에 따라 원하는 자바 설정 파일 이름을 리턴해 줍니다. 여기서 ‘왜 Class 타입이 아니라 String 타입의 배열을 리턴할까요?’라고 질문을 했었는데, 아마도 컴파일 시점에 참조할 수 없는 자바 설정도 동적으로 참조할 수 있도록 하려는 것이 아닌가 싶다는 답변을 들을 수 있었습니다.

이제는 @EnableHello 애노테이션에 ImportSelector 구현체에서 사용할 속성을 추가하고, @Import에다 HelloConfig가 아닌 ImportSelector 구현체를 설정합니다.

그럼 이제 AppConfig에 붙인 @EnableHello의 type 속성을 사용해서 전혀 다른 빈 집합이 등록되게 할 수 있습니다.

그런데, 만약에 옵션에 따라 너무도 다양한 빈 조합이 생긴다면, 이렇게 일일히 여러 빈 설정을 만드는 것도 상당히 번거로울 수 있습니다. 오히려 옵션에 따라 코딩으로 빈을 등록하는 방법도 필요할 수 있겠죠.

[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을 사용해서 중요 로직이 오버라이딩되지 않도록 방지할 수가 없어서, 자칫 오버라이딩으로 인해 중요 로직까지 바뀔 수 있다.

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

[Spring 3.1] TestContext

스프링  3.1 TestContext 변경 내역은 표면적으로 보자면, 크게 두가지입니다.

  • @Configuration 지원
  • @ActiveProfiles 지원

사실 자바 설정은 스프링 3.0 부터 지원했지만, 사용하기 어렵던 이유가, 스프링 TestContext에서 사용하기 번거롭다는 점과, XML 네임스페이스에 해당하는 기능이 없다는 것이었는데, 스프링 3.1부터는 이런 문제가 완전 해결됐습니다.

이런 자바 설정 파일이 있을때, 다음과 같이 테스트를 작성할 수 있습니다.

여기서 주의하실 것은, @ContextConfiguration에는 location이나 classes 둘 중 하나만 사용할 수 있다는 것입니다. 따라서, 여러 종류의 설정 파일, 보통 XML 설정과 자바 설정을 혼용하시겠죠. 그밖에 프로퍼티즈 파일도 사용할 수 있지만.. 좀.. 쓰기가 ㅋ; 암튼, 그런 경우에 설정 파일 중에 하나를 진입점으로 사용해야 합니다.

XML 설정을 진입점으로 사용하려면, 자바 설정을 컴포넌트 스캔을 하거나, 직접 <bean/>을 사용해서 XML 설정에 빈으로 등록해주면 됩니다.

반대로, 자바 설정을 진입점으로 사용하려면, XML 설정을 @ImportResource로 자바 설정에 추가해주면 됩니다.

다음으로 스프링 3.1 TestContext의 프로파일 지원 기능은 스프링 3.1에 추가된 빈 프로파일링 기능을 사용할 때, 해당 프로파일을 손쉽게 테스트할 수 있는 기능입니다.

이렇게 빈 프로파일을 사용한 설정 파일이 있을 때, default 프로파일이 제대로 적용되는지, korea 라는 프로파이일 때 toby라는 id를 가진 빈이 정말 다른 걸로 바뀌는지, 다음과 같이 확인할 수 있습니다.

[스프링 3.1] web.xml이 없는 자바 웹 애플리케이션

Spring 3.1부터 Servlet 3.0을 지원하는데, Servelt 3.0 부터 web.xml 없이 자바 웹 애플리케이션을 배포하는 것이 가능합니다. 따라서 정확히는 스프링 3.1 없이도 web.xml 없는 자바 웹 애플리케이션을 만들 수 있습니다.

그런데.. 조금 귀찮은 부분이 있거든요; 그런 귀찮은 작업을 스프링이 대신 해주고 개발자는 서블릿이나 필터만 등록할 수 있게 해줍니다.

귀찮은 작업엔 뭐가 있냐면… 일단 자바 SPI를 사용하는 방식이기 때문에, META-INF/services 디렉토리에 javax.servlet.ServletContainerInitializer라는 이름의 파일을 만들어 줘야 합니다.

그리고 ServletContainerInitializer의 구현체를 만들고 그 위에 @HandlerTypes라는 애노테이션을 붙이면서 @HandlerTypes 애노테이션 안에.. ServletContainerInitializer의 onStart 메서드의 첫번째 매개변수로 받을 클래스 타입을 명시해 줘야 합니다. 그 타입을 A라고 해보죠.

그럼 이제 A 타입의 구현체를 만들거나, 상속 받은 클래스가 onStart 메서드의 첫번째 매개변수로 전달 됩니다.

귀찮죠;; 그래서 스프링이 다 해뒀고, 우리는 그냥 WebApplicationInitializer 인터페이스만 구현하면 됩니다.

이렇게요. 이게 web.xml을 대신한다고 보면 됩니다.

나머지 XML은 스프링 빈 설정 파일이 있는데, 스프링 3.0부터 이미 자바로 빈 설정을 할 수 있었습니다. 그런데, 이번 스프링 3.1은 그 기능을 대폭 강화하여 XML 네임스페이스에 해당하는 빈 설정도 자바 설정으로 가능하게 해줍니다. 물론 전부는 아니구요. 일부만요… (그래서 사실 스프링 자바 설정을 그리 많이 사용될 것 같진 않다고 생각합니다.)

자바 설정도 xml 처럼 웹용 설정과 비웹용 설정으로 나눠서 설정할 수 있습니다. 이건 Root ACWAC에서 사용할 비웹용 설정이고…

이건 웹용 빈을 설정한 Child ACWAC입니다.

전체 앱은 Github에 올려두었습니다.

https://github.com/keesun/swagger-noxml-mvc

자세한 내용은 이번주 토요일 봄싹 스웨거 책걸이때 말씀드리겠습니다.