요즘엔 3.0의 확장된 IoC/DI기능을 테스트하는 코드를 자주 만들어보는데, DI는 테스트하기가 매우 불편하다.

테스트 하나 당 빈 클래스 두 개 이상(DI하려면 최소 두개는 있어야 하니깐) XML 설정 하나가 필요하기 때문이다. 애노테이션 옵션 하나 바꿔도 다시 새로운 클래스 만들고 XML 만들고 하는 것이 몹시 귀찮다. XML없이 스캔을 해서 쓰는 방법도 있지만 다른 클래스를 로딩 안하게 패키지를 따로 만들어서 클래스를 넣어야 하니 XML 제거하는 것 이상의 불편이 있다.

대충 수백가지 조합이 나올 수 있는 3.0의 DI 방식들을 일일히 그렇게 만들어서 테스트 하자니 몹시 귀찮아하던 차에 테스트 클래스 안에 빈을 스태틱 클래스로 정의해서 사용하는 방법을 생각해보았다. 아무래도 클래스가 한 파일에만 모여있어도 관리하기 쉽다는 장점이 있으니까. 테스트 메소드 앞 뒤에 빈으로 쓸 스태틱 메소드를 선언해두면 한 눈에 보기도 좋고, 학습테스트 만들 때는 그만이다.

스태틱 클래스를 쓰면 클래스 파일 갯수는 줄어들지만 여전히 XML이 필요하다. 게다가 해당 클래스의 스태틱 클래스만 골라서 스캔할 수 없다. 이래 저래 번거롭다고 생각했는데..

 

그런데 오늘 아침 반짝 아이디어가!

AnnotationConfigApplicationContext는 빈 등록으로 두 가지 방식을 지원한다. 하나는 패키지를 넣어서 스캔하는 것과 하나는 @Configuration이 달린 자바콘픽 빈 클래스를 직접 제공하는 것. @Configuration은 그 자체가 하나의 XML파일이나 마찬가지니까 직접 클래스를 넣는 것도 지원해준다.

그런데 스프링은 @Configuration이 달린 클래스는 @Bean이 달린 메소드만 이용해서 빈을 등록하는 것이 아니라, 설정이 담긴 클래스 그 자신도 빈으로 등록한다. 사실은 AnnotationConfigApplicationContext가 해주는 일은 @Configuration 클래스를 넣으면 그냥 그걸 빈으로 등록하는게 전부다. 나머지는 빈 후처리기로 동작하는 각종 Annotation~PostProcessor들에서 등록된 빈을 살펴보고 @Configuration이 달려있는 빈이 있으면 그 메소드를 참조해서 추가로 더 빈을 등록해주는 것이다.

그런데 이 동작방식에 따르면 AnnotationConfigApplicationContext에 스캐닝용 패키지가 아닌 클래스를 직접 넣을 때, 그것이 굳이 @Configuration이 달린 것인지 검사하지 않는다. 물론 JavaDoc에는 @Configuration용이라고 명시하긴 했지만 그걸 무시하고 그냥 그 자체로 빈으로 등록하고 싶은 클래스를 넣어도 안될게 없다.

그래서 XML 없이, 빈 스캐닝도 없이 그냥 직접 등록하고 싶은 클래스를 ACAC에 넣어버린다면 스태틱 클래스를 바로 이용할 수 있다.

그래서 만들어본 초간단 DIJ(JSR-330) @Inject 테스트.

public class InjectTest {
    @Test
    public void inject() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(Hello.class, Printer.class);
        Hello hello = ac.getBean(Hello.class);
        assertThat(hello.printer, is(notNullValue()));
    }

    static class Hello {
        @Inject Printer printer;
    }

    static class Printer {
    }
}

애노테이션을 이용한 DI의 각종 옵션에 대한 테스트 용으로 편리하게 쓸 수 있을 듯.

스태틱 클래스가 자동등록될 때 빈의 이름은 top level class 이름이 추가된다. 따라서 위의 InjectTest$Hello 클래스는 "injectTest.Hello”라는 이름의 빈으로 등록된다.

이름을 직접 지정하고 싶으면 @Component를 이용하거나 DIJ의 @Named를 써도 된다.

@Named(“hello”)

static class Hello { .. }

 

XML에서 스태틱 클래스를 쓸 때는 class=”InjectTest$Hello” 처럼 쓴다. 근데 이 설명이 나와있는 스프링 레퍼런스 문서는 이 것을 inner class라고 표현했는데 틀린 용어다. 자바에서 inner class란 member class 또는 nested class 중에서 static이 아닌 것들만 말한다. 따라서 Inner class 처럼 독자적으로 인스턴스를 만들 수 없는 것들은 스프링의 빈이 될 수 없다. 다들 알아서 이해는 하겠지만… static member class라고 정정해야 할 듯 하다.

Related posts:

  1. 스프링 3.1 (4) Static @Bean 메소드
  2. 테스트 할 수 없는 것을 테스트 하기. Spring ROO와 static method mocking.
  3. 테스트 코드에서 static import를 편하게 넣는 방법
  4. Spring 2.0의 XML확장기능 (1)
  5. Spring 2.0의 XML확장기능 (2)
  6. Spring 2.0 XML확장기능 (3)
  7. Spring 3.0 (56) @Bean 사용의 주의사항
  8. 스프링 3.1 (6) web.xml의 활성 프로파일 설정
  9. Maven settings.xml의 비밀번호 암호화
  10. Spring 3.0 (54) 드디어 등장한 ConfigurationClassApplicationContext
  11. Spring 3.0 (33) JavaConfig의 통합과 변신. 메타-빈(meta-bean) 개념의 등장.
  12. [토스3] 테스트를 위한 필드 주입 유틸
  13. Inside Spring (6) 애노테이션 설정 지원 스프링 웹 테스트용 DispatcherServlet 만들기
  14. Spring 3.0 EL (Spel)을 이용한 AssertThrows 확장 (1)
  15. Spring Expressions(SpEL)를 이용한 Mockito Argument Matcher 만들기

Facebook comments:

to “Spring 3.0 (58) Static Class를 XML없이 빈으로 등록하기”

  1. 테스트 클래스 자체가 @Configuration이 되고 static 클래스들에 @Bean 붙이면 클래스 내부에서 AC를 직접 생성하지 않고 바로 빈들을 받아올 수 있는 건 안될까요? (”)

  2. chanwook/ @Bean은 new XXX()가 들어있는 팩토리 메소드 용이니까 적절치 않지.

    그 보다는 특정 클래스를 지정하면 그 안의 static member class들만 스캐닝 대상으로 하는 빈 스캐너 옵션이 있으면 좋겠어.

  3. 아직 스프링을 제대로 이해하지 못하고 있는 상황이지만, 토비님의 글은 꾸준하게 읽고 있습니다. ^^
    잘 보았습니다.

  4. 허니몬/ 빈약한 글들 열심히 읽어주셔서 고맙습니다.

  5. 요런거 어떤가요. 캬캬

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(loader = StaticMemberClassContextLoader.class)
    public class StaticInnerConfigTest {

    @Autowired ApplicationContext ac;

    static class Hello {
    @Inject Printer printer;
    }

    static class Printer {
    }

    @Test
    public void inject() {
    for(String beanName : ac.getBeanDefinitionNames()){
    System.out.println(beanName);
    }

    Hello hello = ac.getBean(Hello.class);
    assertThat(hello.printer, is(notNullValue()));
    }
    }

    몇일전에 만든 AnnotationContextLoader 확장해서 만들었어요. 음하하핫

  6. 마지막 부분에 nested class에 대한 말씀이 특히 맘에 드는군요.
    사실 Java 프로그래머들중에 static nested class와 inner class 의 구분을
    제대로 못하거나, 혼동해서 쓰는 사람들이 꽤 많더라구요.

    근데, static member class 는 member scope라서 쓰셨겠지만,
    엄밀히는 static nested class 라고 해야겠죠.

    말 나온김에 다른분들과 공유하는 차원에서… :)
    nested class 에는 4종류가 있는데, 아래 링크 따라 가시면,
    잘 분류해 놓은 표를 보실수 있습니다.
    http://java.sun.com/docs/books/tutorial/java/javaOO/summarynested.html

  7. 헐… 스프링 레퍼런스 문서에서도 static inner class 라는 있지도 않은 말이 등장하네요…ㅡ_ㅡ;
    http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-class-ctor
    본문에 말씀하신게 이거였나 보군요.
    가서 딴죽 좀 걸고 와야겠습니다…ㅡ_ㅡ;;;

  8. 재밌는게 Wikipedia에는 Toby님께서 쓰신 static member class 라는 말이 나오긴 하는군요.
    제가 아는한, 언어 스팩이나 The Java Programming Language 같은 책 혹은 Sun의 Java관련
    Tutorial 에서는 쓰이지 않은 단어고, static nested class 라는 말만 쓰인걸로 알고 있는데…
    이건 Wikipedia의 오류로 보여집니다만… 페이지 자체가 Java의 nested class에 대한
    페이지가 아니라 일반적인 inner class에 대한 페이지라서 그런거 같습니다.
    그나저나 static inner class는 완전히 잘못된 말.

  9. Kevin/ Language Specification에서 따왔습니다

    “Member class declarations (§8.5) describe nested classes that are members of the surrounding class. Member classes may be static, in which case they have no access to the instance variables of the surrounding class; or they may be inner classes (§8.1.3).”

    Member class는 nested class를 말하며 static일 수 있다고 했으므로 static nested class와 함께 static member class라고 부르는 것도 스펙으로 볼 때 문제 없다고 보입니다. 정확히 “static nested class”라는 말도 언어 스펙에 나오지 않습니다. 튜토리얼에 나올 뿐이지요.

  10. Toby님 말씀이 맞습니다. 그리고 사용하신 static member class 란말이
    틀렸다는 얘긴 아니었습니다.
    다만 Sun에서 정의한 Java 용어가 static nested class 라는 얘기였습니다.

    (역시 Spring 문서에 나온 static inner class 라는건 틀린 용어지만요).

    위에 인용하신 부분은 member class로 정의된 nested class의 scrope가
    감싼 class의 member라는것에 대한 설명이고, 뒷부분은
    이 member class들이 static 일수도 있고 inner class (non-static) 일수도 있다는 내용에 대한 설명인데
    스펙에는 용어에 대한것 보다는
    사용법이나 동작에 대한 정의가 더 자세한거 같습니다.

    스펙에서는 Nested Class를 크게 3종류 (member classes, local classes, anonymous classes) 로 구분 하고 있습니다만,
    scope, declaration, 사용법, 동작 등에 대한 설명 위주로 나와 있네요.

    말씀하신데로, 사용하신 static member class 가 말이 안 되는것은 아닙니다.
    스펙에서는 static nested class와 inner class를 합쳐서
    member class라고 부르니까요. 사실 스펙을 보면, inner class에 대해서는
    inner class 라는 용어를 사용하는데, static nested class에 대해서는
    이게 좀 애매하죠. 문서내에서 static member class 라고 부르지는 않더군요.
    스펙 3.0의 375 페이지를 보시면
    static nested class 란 말이 한번 사용되었습니다만,
    static member class 라는 말은 한번도 나오지 않더군요.

    아무튼 용어에 대한 더 자세한 설명은 스펙보다
    역시 Sun에서 나온 책중에,
    저자중 한명이 The Java Language Specification 의 저자이자
    Java의 아버지로 불리는 James Gosling 인
    The Java Programming Language (4th edn) 란 책을 보시면,
    133페이지에서 시작하는 챕터 5 전체가
    Nested Classes and Interfaces 에 대한 얘기고,
    소챕터인 5.1 이 Static Nested Types 이고 그 하위의 첫챕터가
    5.1.1 Static Nested Classes 입니다.
    Sun의 공식문서에서 공식적으로 인정한 단어는 Static Nested Class란 얘기죠.

    스펙이나 이책이나 둘다 Sun의 The Java 시리즈이긴 하지만,
    도대체 용어에 대한 설명을 스펙에 전부 다 넣은게 아니라
    다른 책에 넣은 이유는… 책을 하나 더 팔려고 한건지…ㅡ_ㅡ;

    아… 튜토리얼에 나온 자료도 중요한것이,
    이게 Sun에서 Java 교육을 위해 내 놓은
    공식 문서이기 때문에 신뢰성이 있다는 것이죠.
    용어 자체에 대한 설명도 있구요.

    그나저나 Wikipedia를 보니까 External links에 나오는 글들이
    Sun에서 공식적으로 Java에 대해 쓴 문서가 아니라
    외부에서 쓴글들 이더라구요.
    그중 하나는 “static inner class가 때때로 nested class
    라고 불리기도 한다” 는 출처를 알수없는 설명이 있더군요.
    이래서 대학에서는 Wikipedia를 Reference로 사용하지 말라고
    하는거 같습니다.

    아무튼 Toby님 덕분에 용어 사용에 대한 고찰을 다 해보게 되네요. :)
    고맙습니다.

  11. Kevin/ 언어스펙이나 튜토리얼 말고 대부분의 공식 스펙 문에서에 사용되는 “사실상 공식적인 자바 용어는 그냥 static class”입니다. 어짜피 member나 nested 등은 굳이 필요없는 분류를 위해서 사용되는 부가적인 설명일 뿐입니다. Member class가 Nested class의 한 가지이고, 다시 static한 것과 non-static한 것으로 나뉜다는 말이면 static member class라는 표현을 스펙에서 인정한 것이라고 생각합니다. 이미 Language spec.에는 non-static member class라는 표현도 나오니까요.

    SUN의 자바 사이트에 보면 Generics와 Collection 같은 JDK의 설계자 중 한 사람인 Jushua Bloch쓴 Effective Java의 내용이 소개되어있는데, 2판의 22번 아이템을 보시면 “Favor static member classes over nonstatic” 라고 쓰고 있습니다.

    역시 SUN 사이트에 올라와 있는 JDK 1.6의 serialization spec 1.5절에도 “though it is possible to set it for static member classes”라는 표현이 나옵니다.

    SUN의 튜토리얼에서도 발견됩니다. Reflection의 튜토리얼에 보면 역시 “static member classes”라는 표현이 나옵니다.

    따라서 썬이 인정한 공식문서에서 static member class라는 단어가 static nested class 못지않게 사용되며 합법적이라는 생각입니다. static member class, static nested class, static class는 언제든지 서로 교환해서 사용할 수 있습니다. 물론 보통 때라면 그냥 static class라고 쓰겠지만, 이 글에서는 스프링의 “.”이나 “$”을 이용한 표기법을 설명하고자 굳이 다른 클래스의 멤버라는 것을 강조하기 위해서 static member class라고 적은 것 뿐입니다.

  12. 말씀드린데로, 그건 Toby님 말씀이 맞습니다.
    제가 걱정한건 inner class를 static이랑 같이 쓴 말에 대한 잘못에 대한것이 크구요.
    static member class에 대한건 물론 이게 틀렸다는 얘기가
    아니라, 첨언 정도로 쓴것입니다. 짧게 썼다가 결국
    이것에 대한 제 설명이 뒤에 길어지긴 했는데, 공식문서 관련 얘기는,
    스펙문서 자체에서의 용어 정의가 좀 애매 하다는것이었구요.
    스펙상에 mebmer class가 static과 non-static으로 나뉜다는 말은
    있는데, ‘static member class’라는 용어 자체로 쓰인 적이
    없다는 것에 대한것이었구요. 그리고
    용어 관련 얘기는 The Java Programming Language에 더 자세하게
    나오기 때문에 그부분을 언급한 것이구요.

    Joshua Bloch의 Effective Java는 저도 읽었는데,
    읽을때 static nested class로 이해한걸로 기억하고,
    (물론 이 책의 그부분은 Toby님 말씀대로 static member class라고 나오죠
    책 전체에서는 두 용어가 혼용되고 있구요.)
    static member class란 말이 나온게 제대로 기억나지 않는걸로 봐서
    역시 이걸 읽었을때나 지금이나
    static member class가 static nested class와
    다르다는 생각을 가진것은 아니었던 모양입니다.
    뭐 static nested class면 scope이 member가 되기 때문에
    static member class나 static nested class나 그말이 그게 됩니다만,
    Sun에서 정의한, 아… 정확히는
    Sun에서 교육을 목적으로 나온 공식 문서에서
    정의한 용어에 대한 얘길 한것입니다.

    사실 Toby님께서 inner class에 대한 언급없이 static member class라고
    하셨으면, 이말도 그냥 넘어 가지 않았을까 싶네요.
    inner class에 대한 얘길 하다보니 용어에 대해서
    좀더 얘기하다가 stsic nested class 얘기를 하게 된거 같습니다.

    이미 말씀드렸지만, Toby님께서 사용하신말에는 문제가 없다는
    생각이고, 제가 “엄밀히 … 해야겠죠”, 라고 한말이
    static member class를 부정하는듯한 느낌이 난거 같습니다.
    이건 확실히 제 잘못이네요. :) 죄송합니다.

  13. Kevin/ 날카로운 지적을 하신 것인데 죄송할 것 까지야.. :)

    일반적으로는 그냥 static class라고 부르고 inner class는 non-static이다라고 기억해두면 충분할 것 같습니다. 어짜피 static class는 한가지 뿐인데 그걸 굳이 static nested class니 static member class니 하면서 길게 부를 이유가 없지요. 클래스의 종류가 어떤 것이 있는지 공부하려는 때나, 아니면 non-static member class와 대비해서 차이점을 설명할 때를 제외하면요.

  14. 요즘 회사에서 쓸 라이브러리를 만들다 보니까, utility class에
    class instantiation 을 막기 위해서 exception을 던지게 만든
    private constructor 부분의 테스트 커버리지에 빨간색이 자꾸 신경쓰여서
    reflection 을 써서 테스트 코드를 작성했습니다. 근데 하다보니
    이런 테스트가 종종 필요하길래 아예 unaccessible constructor 를
    테스트 하는 패키지를 따로 만들게 됐네요.
    근데 그러다 보니, reflect 을 잔뜩 쓰다가 Class class안에
    isMemberClass() 라는 메소드가 있는걸 발견했습니다.
    어?! 이거 못보던 건데… 하고 보니 J2SE 5.0 부터 추가된 모양입니다.
    그래도 도입된지 꽤 됐는데, 매일 쓰던것만 쓰다 보니 이런걸 놓쳤네요.
    (반성좀 해야겠습니다…ㅠ_ㅠ)

    암튼 이전 댓글에 제가 잔뜩 주절 거려놔서
    이거 정리나 하고 끝내야 겠습니다…ㅡ_ㅡ;

    Java 의 nested Class 에는
    static nested class, inner (non-static) class, local class, anonymous class 의
    4종류가 있으며,
    이들중 scope가 member인 static nested class와 inner class는
    member class 라고도 부른다.
    즉, 가끔 사람들이 inner class라고 잘못 알고 있는 static nested class는
    static nested class 내지 static member class가 맞는 말.
    nested member class와 inner class 의
    Class.isMemberClass() 메소드는 true를 반환하며,
    local class 와 anonymous class는 nested class의 한종류 이기는 하지만,
    member class는 아니기 때문에 이 두 class에 대한
    Class.isMemberClass() 메소드는 false를 반환한다.
    끝.

    Toby님 말씀대로 static nested class 나 static member class 대신에
    이걸 static class 라고 불러도 되겠지만,
    저는 개인적으로는 이말을 사용 안 합니다. 그 이유는…
    Java에서는 아니지만, 비슷한 언어로 자주 같이 거론되는
    C# 에서는 nested static class가 아닌,
    static 멤버만 가질수 있고, new 키워드를 이용한 instance 생성이 불가능한
    static class 가 존재하기 때문에
    그냥 쓰다가 또다른 오해를 불러 일으킬까봐서요…ㅡ_ㅡ;;;
    뭐 이건 Java가 아니라서 크게 상관은 없는 얘깁니다만…

  15. Kevin/ 그나저나 스프링 레퍼런스 문서에서는 수정했나 모르겠군요. 이슈 올리신 거 맞죠?

  16. 네, 올렸는데 아무 소식도 없네요…^^;
    아무래도 최근에 Spring Framework 3.0.0, Spring Security 3.0.0, Spring Roo 1.0.0
    릴리즈 후 한숨 돌리고 있지 않을까 싶어요.
    연말에서 연초로 이어지는 휴가기간 이기도 하겠구요.

  17. [...] Spring 3.0 (58) Static Class를 XML없이 빈으로 등록하기 [...]

  18. cheap mbt shoes on sale Spring 3.0 (58) Static Class를 XML없이 빈으로 등록하기 » Toby’s Epril

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