Spring 설정파일은 결국 bean을 어떻게 등록할 것인가를 결정한다. 따라서 설정파일의 확장기능은 bean등록방법을 효과적으로 확장한 기능이다. 어찌됐든 결과적으로 bean이 등록이 되므로 XML확장기능을 잘 사용하려면 Spring의 기본적인 bean등록기능을 알아야 한다.

Spring에 등록되는 bean은 다양한 기준으로 구분될 수 있다.

가장 대표적인 구분은 bean이 root bean인가 child bean인가하는 것과 top leve bean인가 inner bean인가 이다. 각각 조합하면 4가지가 가능하겠다.

이 기준에 따라 custom tag 한개를 기준으로 등록되는 빈이 top level이 몇개인지, 각각의 빈이 root인지 child인지로 구분해서 생각해볼 수 있다.

또 bean의 property가 primitive type으로만 되어있는지 다른 bean에 대한 ref.를 가지고 있는지도 생각해볼 수 있다.

Tag당 bean이 여러개라면 그 그룹안에서 서로 의존관계가 있는지 parent/child관계가 있는지 의존관계가 top level끼리인지 top/inner사이에 있는지 아니면 해당 tag로 등록되는 bean들외에 다른 bean에 대한 참조를 가질 수 있는지 등으로 구분해서 생각해볼 수 있다.

복잡해보이지만 사실 Spring의 bean의 종류를 잘 알면 구분하는 것이 어렵지 않다.

 

BeanDefinitionParser

새로운 tag를 추가할 때 필요한 코드의 핵심은 BeanDefinitionParser이다. BeanDefinitionParser는 Spring설정 XML을 분석해서 bean의 메타정보인 BeanDefinition을 만들어내는 역할을 담당한다.

public interface BeanDefinitionParser { 
  BeanDefinition parse(Element element, ParserContext parserContext);
}

파라메터로 넘어오는 것은 XML element 정보와 현재 XML의 parsing을 담당하고 있는 파서에 대한 컨텍스트정보이다. 이를 이용해서 생성해야 할 bean에 대한 등록정보인 BeanDefinition을 넘겨준다.

BeanDefinitionParser를 구현한 Spring내의 클래스는 sandbox에 있는 것과 inner class를 포함해서 30개 가까이 된다. BeanDefinitionParser를 직접 구현하는 것이 어렵지는 않지만 개발자가 직접 custom tag를 개발할 때 유용한 몇가지 abstract class등은 알아두는 것이 좋다.

이중 가장 손쉽게 활용할 수 있는 것은 AbstractBeanDefinitionParser이다.

AbstractBeanDefinitionParser는 단일 top-level  빈을 가진 한개 또는 여러개의 빈을 생성할 때 사용할 수 있다. 단일 top-level을 가졌다는 뜻은 bean id를 직접 부여할 수 있는 것이 한개뿐이라는 것이다. 물론 top-level이지만 id가 없을 수도 있다. 해당 custom tag에서는 top-level이지만 다른 빈에 중첩되는 것은 가능하다는 뜻이다. 나머지는 inner bean으로 자동등록된다. 사실 inner bean이라고 id가 없는 것은 아니다. 단지 개발자가 지정하지 못하고 자동생성될 뿐이다.

AbstractBeanDefinitionParser의 서브클래스중 AbstractSingleBeanDefinitionParser는 딱 한개의 top-level 빈만을 생성하는 경우 사용할 수 있다.

더 나가서 AbstractSimpleBeanDefinitionParser는 애트리뷰트와 프로퍼티를 일치하게 하는 (결국 XML파싱과 pvs설정의 수고를 좀 덜어주는) 방식을 사용하는 가장 간단한 케이스에 사용할 수 있는 클래스이다.

각 클래스의 사용법은 친절한 API와 Spring에서 사용한 케이스를 분석해보면 사용법을 간단히 알 수 있다.

 

AbstractSingleBeanDefinitionParser

Spring Reference에 나오는 케이스를 간단히 보자.

만들려고 하는 tag는 SimpleDateFormat 타입의 빈을 생성하고 애트리뷰트로 pattern을 지정할 수 있는 <x:dateformat /> 태그이다.

네임스페이스는 myns로 설정하고 작업한 예를 보면

<myns:dateformat id=”myDateFormat” pattern=”yyyy-MM-dd” />

글로벌한 설정이 아니라 구체적으로 다른 빈에서 사용해야할 빈이기 때문에 id를 직접 지정할 수 있게 한다.

이 XML element를 읽어서 BeanDefinition을 돌려주는 코드는 다음과 같다.

public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

  public void doParse(Element element, BeanDefinitionBuilder bean) {
    String pattern = element.getAttribute(“pattern”);
    bean.addConstructorArg(pattern);
    String lenient = element.getAttribute(“lenient”);
    if (StringUtils.hasText(lenient)) {
      bean.addPropertyValue(“lenient”, Boolean.valueOf(lenient));
    }
  }

  protected Class getBeanClass(Element arg0) {
     return SimpleDateFormat.class;
  }
}

단일 빈을 생성할 때 사용하는 AbstractSingleBeanDefinitionParser를 이용하면 builder패턴을 적용해서 BeanDefinition을 손쉽게 다룰 수 있는 BeanDefinitionBuilder를 파라메터로 넘겨준다. 남은 할 일은 element에서 tag정보(<myns:dateforamt… />)를 읽어서 bean 메타정보를 추가해주는 것이다. 재밌게도 SimpleDateFormat은 생성자를 이용해서 패턴정보를 받는다. 따라서 addConstructorArg()를 이용해서 constructor setter설정을 추가한다. BeanDefinitionBuilder를 사용하는 것은 <bean id … class ..>를 코드에 의해서 설정한다고 생각하고 하면 아주 간단하다.

 

AbstractBeanDefinitionParser

AbstractBeanDefinitionParser은 한개 또는 여러개 빈을 설정할 때 사용한다. 단 top-level(id부여가능) 빈은 하나뿐이다.

이를 이용한 bean definition parser가 구현해야 할 것은 다음의 template method이다.

protected abstract AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext);

XML element정보와 parser context를 받는 것은 동일하다. 리턴하는 것은 단일 BeanDefinition정보이다. 여기서 리턴하는 것이 top-level의 bean이다. 만약 해당 custom tag에 id를 부여했을 경우 이 id를 통해서 가져오는  바로 그 빈이다.

그럼 다른 부가적인 빈이 있다면? 그것은 inner bean의 형태로 등록이 된다. inner bean이 된다는 것은 top-level빈의 property로 inner bean이 설정된다는 것이다. inner bean의 등록에는 중첩정도나 갯수의 제한이 없다.

테스트하려고 만든 간단한 bean설정을 보자.

BeanA는 BeanB에 의존적인 간단한 bean이다.

public class BeanA {
  public String value;
  BeanB beanB;
  ..
}

BeanB는 아주 간단한 빈이다.

public class BeanB {
  String value;
  …
}

다음과 같은 태그를 써서 id가 부여된 BeanA가 만들어지고 동시에 BeanB도 하나 만들어져 inner bean으로 BeanA에 연결이 되게 하려고 한다. 동시에 BeanA와 BeanB의 value값은 직접 다 설정할 것이다. 결과는 다음과 같은 tag 사용이 가능하다.

<myns:beanab id=”myBeanA” valuea=”A” valueb=”B” />

Custom tag를 사용하지 않는다면

<bean id=”myBeanA” class=”…BeanA”>
  <property name=”value” value=”A” />
  <property name=”beanB”>
     <bean class=”…BeanB”>
        <property name=”value” value=”B” />
     </bean>
  </property>
</bean>

이라고 써야할 것이다.

bean설정을 직접 만들 때는 BeanDefinition을 구현한 클래스의 인스턴스를 생성하면 된다.

BeanDefinition에는 크게 RootBeanDefinition과 ChildBeanDefinition이 있다. Child는 parent를 가진 상속 bean이라고 보면 된다.

여기서는 당연히 RootBeanDefinition을 사용하면 된다.

생성방법은 1) RootBeanDefinition 인스턴스를 만들고 2) Source를 세팅하고 3) 필요한 프로퍼티를 추가한다. 아주 간단하다.

BeanB를 생성하는 코드는

RootBeanDefinition beanBDef = new RootBeanDefinition(BeanB.class);
beanBDef.setSource(parserContext.extractSource(element));
beanBDef.getPropertyValues().addPropertyValue(“value”, element.getAttribute(“valueb”));

마지막 줄의 addPropertyValue를 통해서 이 생성된 빈의 실제 프로퍼티값을 추가한다. 값은 XML attribute에서 가져오면 된다. 이렇게 BeanB에 대한 생성이 완료됐다. 하지만 아직 이 빈이 BeanFactory에 등록된 것은 아니다. 메타정보만 생성한 것이다.

다음은 BeanA이다.

RootBeanDefinition beanADef = new RootBeanDefinition(BeanA.class);
beanADef.setSource(parserContext.extractSource(element));
beanADef.getPropertyValues().addPropertyValue(“value”, element.getAttribute(“valuea”));
beanADef.getPropertyValues().addPropertyValue(“beanB”, beanBDef);

BeanB와 설정방법은 비슷하다. 다만 BeanA는 BeanB타입의 레퍼런스 프로퍼티를 가지고 있다. 이 것을 설정하는 것은 위에서 만든 BeanB설정정보를 addPropertyValue에 넣어주면 된다. 역시 간단하다.

이렇게 해서 BeanA, BeanB의 설정을 마쳤다. 여기서 top-level인 BeanA의 설정을 리턴해주면 된다.

이렇게해서 완성된 코드는

public class BeanABBeanDefinitionParser extends AbstractBeanDefinitionParser {
protected AbstractBeanDefinition parseInternal(Element element,
ParserContext parserContext) {
  // beanB
  RootBeanDefinition beanBDef = new RootBeanDefinition(BeanB.class);
  beanBDef.setSource(parserContext.extractSource(element));
  beanBDef.getPropertyValues().addPropertyValue(“value”, element.getAttribute(“valueb”));

 // beanA
  RootBeanDefinition beanADef = new RootBeanDefinition(BeanA.class);
  beanADef.setSource(parserContext.extractSource(element));
  beanADef.getPropertyValues().addPropertyValue(“value”, element.getAttribute  (“valuea”));
  beanADef.getPropertyValues().addPropertyValue(“beanB”, beanBDef);

  return beanADef;
}
}

parseInternal()을 호출한 AbstractBeanDefinitionParser의 코드에서는 리턴된 beanA의 설정정보를 BeanFactory에 등록을 한다.

그럼 BeanB는?

BeanB는 AbstractBeanDefinitionParser의 shouldFireEvents 프로퍼티가 세팅되어있는 경우에만 등록을 한다.

개념은 이렇다. BeanA는 해당 custom tag의 top-level bean이다. 만약 태그 하나의 두개 이상의 빈이 동시에 등록되는 것이라면 이 빈들은 하나의 component같은 개념으로 묶일 수 있다. 이에 따라 Spring 2.0에는 BeanComponentDefinition이라는 새로운 그룹핑된 빈설정 클래스를 추가했다. shouldFireEvents 프로퍼티는 top-level의 모든 inner bean과 ref. bean을 조사해서 그것을 bean component개념으로 묶어주고 전체 component에 속한 bean을 모두 BeanFactory에 등록해준다.

어쨌든 역시 심플하게 만들 수 있다. Parser를 만드는 것보다 사실 tag/bean관계를 설계하는 것이 더 어렵고 중요한 작업일 것이다.

AbstractBeanDefinitionParser의 기본등록작업을 처리하는 코드를 잘 살펴보면 BeanDefinition에 id정보를 추가해서 BeanDefinitionHolder에 넣고 이를 BeanDefinitionReaderUtils.registerBeanDefinition()에 넘겨서 최종적으로 등록을 한다. 등록할 registry(bean factory)정보를 참조하기 위해서 parser context에서 registry정보를 가져와 함께 넘긴다.

BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());

이 작업이 모든 inner bean, ref bean에 일어나는 과정을 미리 만들어 둔 것이 AbstractBeanDefinitionParser이다.

그렇다면 하나의 태그로 여러개의 빈을 만드는데 서로 ref.관계가 없는 id를 가진 top-level빈을 여러개 만들어야 한다면 어쩔 것인가? 이때는 AbstractBeanDefinitionParser를 쓸 수 없다. 이 경우라면 직접 BeanDefinitionParser인터페이스를 구현해서 작업을 해야 한다.

직접 등록까지 마쳐야 하니 매 빈에 걸쳐서 다음의 작업을 해야한다.

  1. BeanDefinition 생성
  2. ID를 부여하고 BeanDefinitionHolder를 생성
  3. Registry에 BeanDefinitionHolder를 등록

물론 inner bean등이 있다면 좀 더 복잡한 등록작업을 해야한다.

사실 하나의 태그로 직접 연관관계가 없는 빈들을 등록하는 것은 일반적인 케이스가 아니기 때문에 이를 수월하게 해줄 Spring 클래스는 없다. 이런 경우는 게으른 개발자(나)가 convention이 잘 부여된 비슷한 이름이나 클래스를 가진 여러개의 빈 등록을 한방에 하기 위해서 사용할 때 쓰게 될 것이다.

샘플 예를 보자.

이번엔 BeanC를 정의했다.

public class BeanC {
  String value; 
  …
}

 

BeanDefinitionParser 직접 이용하기

하나의 태그로 BeanC타입의 빈을 각각 다른 아이디를 부여해서 두개를 만드는 tag를 만들어보자.

<myns:twobeanswithid id1=”beanC1″ id2=”beanC2″ value1=”A” value2=”B” />

이렇게 정의하면

<bean id=”beanC1″ class=”sample.namespace.BeanC”>
   <property name=”value” value=”A” />
</bean>
<bean id=”beanC2″ class=”sample.namespace.BeanC”>
   <property name=”value” value=”B” />
</bean>

이렇게 만들어지는 것이다.
 
Parser는 다음과 같이 만들면 된다.

public class TwoBeansWithIdBeanDefinitionParser implements BeanDefinitionParser {

public BeanDefinition parse(Element element, ParserContext parserContext) {
  RootBeanDefinition bean1 = new RootBeanDefinition(BeanC.class);
  bean1.setSource(parserContext.extractSource(element));
  bean1.getPropertyValues().addPropertyValue(“value”, element.getAttribute(“value1″));
  BeanDefinitionHolder beanDefinitionHolder1 =
    new BeanDefinitionHolder(bean1, element.getAttribute(“id1″));
  BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder1,
    parserContext.getRegistry());

  RootBeanDefinition bean2 = new RootBeanDefinition(BeanC.class);
  bean2.setSource(parserContext.extractSource(element));
  bean2.getPropertyValues().addPropertyValue(“value”, element.getAttribute(“value2″));
  BeanDefinitionHolder beanDefinitionHolder2 =
    new BeanDefinitionHolder(bean2, element.getAttribute(“id2″));
  BeanDefinitionReaderUtils.registerBeanDefinition(beanDefinitionHolder2,
    parserContext.getRegistry());
  return bean1;
}
}

리팩토링을 해서 더 간결하게 할 수 있지만 원리를 보기 위해 그냥 두개의 등록과정을 그대로 사용했다. 역시 간단하다.

 

RuntimeBeanReference

마지막으로 BeanDefinition의 프로퍼티를 설정할 때 만약 ref관계에 있는 다른 id만 아는 bean의 reference를 넣어주어야 할 경우가 있다.

<myns:mytag id=”abc” … cref=”myBeanC” />
<bean id=”myBeanC” … />

이런케이스이다. 이 경우 mytag에 해당하는 BeanDefinition을 만들고 addPropertyValue를 해줄 때 두번째 파라메터에 다른 bean definition대신에 runtime bean definition정보를 가져와서 설정하게 해주면 된다.

myTagBeanDef.getPropertyValues().addPropertyValue(“beanC”,
new RuntimeBeanReference(element.getAttribute(“cref”)));

이렇게 하면 cref attribute의 값에 해당하는 id를 가진 다른 bean을 검색해서 이 레퍼런스 정보를 프로퍼티에 세팅해준다.

 

이정도면 BeanDefinitionParser를 사용하는 모든 케이스를 다 알아봤다. 남은 것은 다양한 스타일의 빈을 설계하고 이를 설정하는 코드들을 만들어보는 것.

Spring에 이미 등록되어있는 각종 BeanDefinitionParser들의 소스를 분석해보는 것이 많은 도움이 될 것이다. 또 이를 잘 살펴보면 BeanFactory와 BeanDefinition같은 Spring IoC Container의 핵심구현방법에 대해서도 이해할 수 있게 될 것이다.

Related posts:

  1. Spring 2.0 XML확장기능 (3)
  2. Spring 3.0 (58) Static Class를 XML없이 빈으로 등록하기
  3. Spring 2.0의 XML확장기능 (1)
  4. 스프링 3.1 (6) web.xml의 활성 프로파일 설정
  5. Maven settings.xml의 비밀번호 암호화
  6. Spring 3.0 (48) OXM모듈의 JAXB2 사용시 주의할 점
  7. Spring 상식퀴즈 (1) – DI 태클하기
  8. Spring 3.0 (56) @Bean 사용의 주의사항
  9. S1A 2008 셋째날 – Spring JavaConfig
  10. Inside Spring (5) PropertyPlaceholderConfigurer를 @Bean으로 정의해서는 안되는 이유
  11. Spring 3.0 EL (Spel)을 이용한 AssertThrows 확장 (1)
  12. InsideSpring (1) Annotated Factory Method (@Configuration)을 쓰는 4가지 방법 (3)
  13. InsideSpring (1) Annotated Factory Method (@Configuration)을 쓰는 4가지 방법 (1)
  14. Google App Engine for Java에서 Spring 3.0 사용하기
  15. Spring 3.0 (54) 드디어 등장한 ConfigurationClassApplicationContext

Facebook comments:

to “Spring 2.0의 XML확장기능 (2)”

  1. that may be the end of this post. Here youll find some web-sites that we assume you will appreciate, just click the links over

  2. Every after in a even though we pick blogs that we study. Listed beneath would be the newest web-sites that we choose

  3. please pay a visit to the internet sites we adhere to, like this a single, as it represents our picks in the web

  4. Hello there, just became aware of your blog through Google, and found that it’s truly informative. I’m going to watch out for brussels. I’ll appreciate if you continue this in future. Lots of people will be benefited from your writing. Cheers!

  5. I really liked your blog post.Much thanks again. Much obliged.

  6. What as up I am from Australia, this time I am viewing this cooking related video at this web page, I am really happy and learning more from it. Thanks for sharing.

  7. Hi there I am so excited I found your webpage,
    I really found you by error, while I was browsing on Aol for something else, Regardless I am
    here now and would just like to say thank you for a tremendous post and a all round thrilling blog (I also love the theme/design), I don’t have time to look over it all at the moment but I have bookmarked it and also added your RSS
    feeds, so when I have time I will be back to read much more, Please do keep up the superb work.

  8. Wonderful story, reckoned we could combine a few unrelated data, nevertheless really really worth taking a appear, whoa did 1 discover about Mid East has got more problerms also

  9. Im no professional, but I suppose you just crafted the best point. You definitely comprehend what youre talking about, and I can truly get behind that. Thanks for staying so upfront and so honest.

  10. Just Browsing While I was surfing today I saw a great post about

  11. Thanks again for the article.Much thanks again. Fantastic.

  12. Thanks a lot for the post.Thanks Again. Really Cool.

  13. here are some links to internet sites that we link to mainly because we think they are worth visiting

  14. Nice respond in return of this query with genuine arguments and describing all on the topic of that.

  15. Hello there! Do you use Twitter? I’d like to follow
    you if that would be ok. I’m definitely enjoying your blog and look forward to new updates.

  16. we like to honor lots of other online sites around the internet, even when they arent linked to us, by linking to them. Under are some webpages really worth checking out

  17. usually posts some incredibly intriguing stuff like this. If you are new to this site

  18. the time to study or take a look at the content material or web pages we’ve linked to beneath the

  19. although sites we backlink to beneath are considerably not related to ours, we really feel they’re really worth a go by means of, so possess a look

  20. This particular blog is really interesting and also amusing. I have chosen many handy tips out of this blog. I ad love to come back again and again. Thanks a lot!

  21. just beneath, are numerous entirely not associated internet sites to ours, on the other hand, they’re surely really worth going over

  22. Thanks again for the article post.Really looking forward to read more. Great.

  23. Looking forward to reading more. Great post.Really thank you! Awesome.

  24. Thanks, I’ve just been searching for information about this subject for ages and yours is the best I’ve came upon so far. However, what in regards to the conclusion? Are you positive in regards to the supply?

  25. check below, are some entirely unrelated websites to ours, nevertheless, they are most trustworthy sources that we use

  26. I’ve recently started a web site, the info you offer on this web site has helped me greatly. Thank you for all of your time & work. “There can be no real freedom without the freedom to fail.” by Erich Fromm.

  27. Whoa! This blog looks just like my old one! It as on a totally different subject but it has pretty much the same layout and design. Wonderful choice of colors!

  28. We’re a group of volunteers and opening a new scheme in our community. Your website offered us with valuable info to work on. You’ve done an impressive job and our entire community will be thankful to you.

  29. one of our visitors lately recommended the following website

  30. please take a look at the web sites we stick to, like this 1, as it represents our picks through the web

  31. weblink I want to start to put all my photos up on my camera, and start a blog or something. Where is a good place to do this like a website or something, do i have to copyright them thanks :) .

  32. Wonderful story, reckoned we could combine a number of unrelated data, nevertheless truly really worth taking a look, whoa did a single master about Mid East has got additional problerms at the same time

  33. below youll come across the link to some internet sites that we consider you ought to visit

  34. You obviously know your stuff. Wish I could think of something clever to write here. Thanks for sharing.

  35. here are some hyperlinks to sites that we link to mainly because we think they’re really worth visiting

  36. usually posts some very exciting stuff like this. If you are new to this site

  37. Thanks for sharing superb informations. Your website is very cool. I’m impressed by the details that you have on this website. It reveals how nicely you perceive this subject. Bookmarked this website page, will come back for more articles. You, my friend, ROCK! I found just the info I already searched all over the place and simply couldn’t come across. What a great site.

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