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. [...] 두 번째 세션은 이일민대표가 스프링2.0에서 새로 도입된 스키마 기반의 빈 설정과 커스텀 스키마 작성에 대해서 발표하였습니다. 조금 생소한 분들이 있을만한 주제였지만, 적절한 데모와 함께 이일민대표의 차분한 진행에 대해서 많은 분들이 만족스러워하셨습니다. 스스로 실습해보고 싶은 분들은 이일민대표의 블로그에 연재된 글(1회, 2회, 3회)을 참조해서 도전해보세요. [...]

  2. вааааааа не то что улыбнуло оборвало полностью супер просто давай исчо

  3. 9ChG3b xmmqmlozqtua, [url=http://xrdfsytukoic.com/]xrdfsytukoic[/url], [link=http://gmuzatwnqhjn.com/]gmuzatwnqhjn[/link], http://aafbdleilwcn.com/

  4. Browse Around THIS Web-Site

  5. What state does PA stand for? I would be extremely valuable if any individual can react to that issue. I’m trapped with this problem for final 7 days and I’m not able to locate homes reply for that situation. Any response will be hugely appreciated. Thank you quite a lot and have a excellent day. Apologies for my weak english language. Cheers !
    _________________
    Backlinks

  6. Does white bread mold faster that wheat bread? I would be quite beneficial if any individual can reply to that question. I’m trapped with this difficulty for previous 7 days and I’m not ready to locate houses reply for that situation. Any response will be highly appreciated. Thank you quite considerably and have a excellent working day. Apologies for my weak english language. Cheers !
    _________________
    Refluks żołądkowy

  7. What are different types of physical properties? I would be really valuable if anyone can react to that issue. I’m caught with this difficulty for last week and I’m not in a position to discover houses solution for that problem. Any reaction will be very appreciated. Thank you very considerably and have a wonderful working day. Apologies for my weak english language. Cheers !
    _________________
    choroba refluksowa przełyku

  8. Witch is faster light or sound? I would be really useful if anybody can reply to that query. I’m trapped with this problem for final week and I’m not capable to discover houses answer for that concern. Any response will be very appreciated. Thank you very considerably and have a excellent day. Apologies for my weak english language. Cheers !
    _________________
    zgaga objawy refluks żołądkowo przełykowy choroba przełyku

  9. Hi just wanted to give you a quick heads up and let you know a few of the pictures aren’t loading properly. I’m not sure why but I think its a linking issue. I’ve tried it in two different internet browsers and both show the same outcome.

  10. Is it true that the more matter an object has the greater the pull of gravity on the object? I would be really valuable if anyone can answer to that question. I’m trapped with this problem for final week and I’m not able to discover houses response for that concern. Any reaction will be extremely appreciated. Thank you quite considerably and have a wonderful working day. Apologies for my weak english language. Cheers !
    _________________
    http://neverwinternights2download.blogspot.com/2013/08/neverwinter-nights-2-download.html

  11. Hi, extremely great website. Lastly any person provides useful data.

    http://chorobarefluksowa.blogspot.com/

  12. This piece of writing is truly a nice one it helps
    new the web people, who are wishing in favor of blogging.

  13. A good quality basis designed for writing “triggers” is the chat negotiations that receive lay on top of Facebook and emerge resting on your Newsfeed casino. The following is a segment beginning just one of these:
    “…bingo and the church. isn’t gambling a sin up till now it’s c4sin00n0n used as a fundraiser designed for the church?”
    Bingo is positively a form of betting for the reason that the players add a fee in arrange to play a game of chance to win larger sums of money. Lotteries are gambling other than they have develop into socially suitable since they are imaginary to live only if funds for commendable causes but the players are only tiresome to win the bigger prizes that are happy to an very little numeral of participants.
    And, depending ahead where you live, mount racing, plague racing, slot equipment, poker, sports outcomes and many additional tricks have turn out to be socially good enough forms of gambling casino. Even the Internet provides access to these forms of gambling with untrustworthy degrees of access all the way through the world.

  14. thank you for share!

  15. Look Here
    [url=http://www.jiujiumeiwen.com/include/air-max-cerceta.html]nike air max baratas[/url]
    nike air max baratas

  16. thank you for share!

  17. Why Not Try HERE
    [url=http://www.hyhzfp.com/images/headnewbgk.php?/kansas-city-chiefs-new-uniforms-it-looks-so-comfortable-and-cool-with-no-sense-of-depressing.html]kansas city chiefs new uniforms-It looks so comfortable and cool with no sense of depressing[/url]
    kansas city chiefs new uniforms-It looks so comfortable and cool with no sense of depressing

  18. You Could Try These Out
    [url=http://jxqmj.com/member/mystowp.php?/where-to-buy-louboutin-shoes-is-one-of-a-new-style.html]where to buy louboutin shoes-Is one of a new style[/url]
    where to buy louboutin shoes-Is one of a new style

  19. thank you for share!

  20. Go To This Web-site
    [url=http://outlettonlineshop.com]ugg online[/url]
    ugg online

  21. You Can Check Here
    [url=http://bootssaleukonline.com]ugg australia uk[/url]
    ugg australia uk

  22. mbt.com Spring 2.0의 XML확장기능 (2) » 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