Outsider's Dev Story

Stay Hungry. Stay Foolish. Don't Be Satisfied.
RetroTech 팟캐스트 44BITS 팟캐스트

[Spring 레퍼런스] 부록 C. 확장 가능한 XML 작성하기

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.



부록 C. 확장 가능한 XML 작성하기

D.1 소개

2.0 이후 스프링은 빈을 정의하고 구성할 때 스키마에 기반을 두고 기본 스프링 XML 형식을 확장하는 기능을 제공한다. 이번 장에서는 자신만의 커스텀 빈 정의를 작성하는 방법과 이러한 파서를 스프링 IoC 컨테이너와 통합하는 방법에 대해서 자세히 다룬다.

스키마를 이해하는 XML 에디터로 설정파일을 작성할 수 있도록 스프링의 확장성 있는 XML 구성 메커니즘은 XML 스키마에 기반을 두고 있다. 표준 스프링 배포판에 포함된 현재 스프링 XML 구성 확장에 익숙하지 않다면 Appendix C, XML 스키마에 기반을 둔 구성 부록을 먼저 보기 바란다.

다음의 (각각) 간단한 과정을 통해 새로운 XML 구성 확장을 만들 수 있다.

  1. 커스텀 요소를 설명하는 XML 스키마를 작성한다.
  2. 커스텀 NamespaceHandler 구현체를 작성한다. (간단하므로 걱정하지 마라.)
  3. 하나 이상의 BeanDefinitionParser 구현체를 작성한다.(여기서 실제 동작이 일어난다.)
  4. 스프링에 위의 아티팩트(artifact)를 등록한다.(이 과정도 쉽다.)

각 단계에 다른 설명은 이어서 할 것이다. 예를 들어 간단하게 (java.text 패키지의) SimpleDateFormat타입의 객체를 구성할 수 있게 하는 XML 확장(커스텀 XML 요소)을 생성할 것이다. 생성을 완료했을 때 다음과 같이 SimpleDateFormat 타입의 빈 정의를 정의할 수 있다.

<myns:dateformat id="dateFormat"
    pattern="yyyy-MM-dd HH:mm"
    lenient="true"/>

(이 예제가 너무 간단하다고 걱정하지 마라. 뒤에서 더 자세한 예제를 다룰 것이다. 이 첫 예제의 의도는 기본적인 각 단계를 한번 해보는 것이다.)

D.2 스키마 작성

스프링 IoC 컨테이너와 사용하는 XML 구성 확장을 작성하려면 확장에 대해서 설명하는 XML 스키마를 먼저 작성해야 한다. 다음은 SimpleDateFormat 객체를 구성하는데 사용할 스키마이다.




<xsd:schema xmlns="http://www.mycompany.com/schema/myns"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:beans="http://www.springframework.org/schema/beans"
  targetNamespace="http://www.mycompany.com/schema/myns"
  elementFormDefault="qualified"
  attributeFormDefault="unqualified">

  <xsd:import namespace="http://www.springframework.org/schema/beans"/>

  <xsd:element name="dateformat">
    <xsd:complexType>
       <xsd:complexContent>
          <xsd:extension base="beans:identifiedType">
             <xsd:attribute name="lenient" type="xsd:boolean"/>
             <xsd:attribute name="pattern" type="xsd:string" use="required"/>
          </xsd:extension>
       </xsd:complexContent>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

(강조한 라인은 구분할 수 있는 모든 태그의 extension base를 담고 있다.(이는 컨테이너에서 빈의 구분자로 사용할 id 속성이 있다는 말이다.) 스프링이 제공한 'beans' 네임스페이스를 임포트했으므로 이 속성을 사용할 수 있다.)

<myns:dateformat/> 요소를 사용해서 XML 애플리케이션 컨텍스트 파일에서 직접 SimpleDateFormat 객체를 구성할 때 위의 스키마를 사용할 것이다.

<myns:dateformat id="dateFormat"
    pattern="yyyy-MM-dd HH:mm"
    lenient="true"/>

인프라스트럭처 클래스를 생성한 후에는 위 XML이 다음의 XML 코드와 본질에서 완전히 같다. 다시 말하면 두 프로퍼티 셋으로 'dateFormat'라는 이름으로 구분하는 SimpleDateFormat 타입을 컨테이너에서 빈을 생성한 것이다.

<bean id="dateFormat" class="java.text.SimpleDateFormat">
    <constructor-arg value="yyyy-HH-dd HH:mm"/>
    <property name="lenient" value="true"/>
</bean>
Note
구성 형식을 생성할 때 스키마에 기반을 둔 접근을 통해 스키마를 이해하는 XML 에디터를 가진 IDE와 강력하게 통합할 수 있다. 적절하게 작성된 스키마를 사용하면 목록에서 정의된 여러 구성 옵션 중에서 사용자가 자동완성을 사용할 수 있다.


D.3 NamespaceHandler 작성

스키마에 추가적으로 NamespaceHandler가 필요하다. NamespaceHandler는 구성 파일을 파싱하는 동안 스프링이 만나는 이 네임스페이스의 모든 요소를 파싱할 것이다. 이 예제에서 NamespaceHandler는 myns:dateformat를 파싱해야 한다.

NamespaceHandler는 딱 세가지 메서드를 제공하는 간단한 인터페이스이다.

  • init() - NamespaceHandler를 초기화할 수 있고 핸들러를 사용하기 전에 스프링이 호출한다.
  • BeanDefinition parse(Element, ParserContext) - 스프링이 최상위 요소(빈 정의나 다른 네임스페이스 하위에 있지 않은)를 만났을 때 호출된다. 이 메서드는 빈 정의를 등록하고 빈 정의를 반환할 수 있다.
  • BeanDefinitionHolder decorate(Node, BeanDefinitionHolder, ParserContext) - 스프링이 속성이나 다른 네임스페이스 하위에 있는 요소를 만났을 때 호출된다. 하나 이상의 빈 정의에 데코레이션을 스프링 2.0의 스코프 지원으로 예제에 사용했다. 데코레이션을 사용하지 않고 간단한 예제로 시작하지만 뒤에 나오는 고급 예제에서 데코레이션을 보여줄 것이다.

모든 네임스페이스에 대한 NamespaceHandler를 작성하는 것이 가능하지만, 스프링 XML 구성 파일에서 각 최상위 XML 요소가 하나의 빈 정의가 되는 경우이다.(이 예제처럼 하나의 <myns:dateformat/> 요소가 하나의 SimpleDateFormat 빈 정의가 되는 경우를 말한다.) 스프링은 이 시나리오를 지원하는 편의 클래스를 다수 제공하고 있다. 이 예제에서 NamespaceHandlerSupport 클래스를 사용할 것이다.

package org.springframework.samples.xml;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class MyNamespaceHandler extends NamespaceHandlerSupport {
  public void init() {
    registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
  }
}

주의력이 깊은 사람이라면 이 클래스에 파싱 로직이 많지 않다는 것을 눈치챌 것이다. 사실 NamespaceHandlerSupport 클래스는 위임의 개념을 사용해서 네임스페이스의 요소를 파싱해야 할 때 이를 위임하는 다수의 BeanDefinitionParser 인스턴스 등록을 지원한다. 이는 관심사를 깔끔하게 분리해서 NamespaceHandler가 네임스페이스의 모든 커스텀 엘리먼트의 다양한 파싱을 다룰 수 있게 하면서 복잡한 XML 파싱 작업은 BeanDefinitionParsers에 위임한다. 다음 과정에서 볼 수 있듯이 이는 각 BeanDefinitionParser가 단인 커스텀 요소를 파싱하는 로직만 가지면 된다는 의미이다.

D.4 BeanDefinitionParser 작성하기

NamespaceHandler가 특정 빈 정의 파서(이 예제에서는 'dateformat')가 매핑된 XML 요소를 만났을 때 BeanDefinitionParser를 사용할 것이다. 다시 말하면 BeanDefinitionParser는 스키마에 정의된 개별적인 최상위 XML 요소의 파싱을 담당한다. 이 파서에서 XML 요소에 접근하므로(하위 요소에도 접근한다.) 다음 예제에서 볼 수 있듯이 커스텀 XML 컴텐츠를 파싱할 수 있다.

package org.springframework.samples.xml;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

import java.text.SimpleDateFormat;

public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 
  protected Class getBeanClass(Element element) {
    return SimpleDateFormat.class; 
  }

  protected void doParse(Element element, BeanDefinitionBuilder bean) {
    // 스키마에서 명시적으로 값이 제공되어야 한다고 되어 있으므로 이는 절대 null이 될 수 없다
    String pattern = element.getAttribute("pattern");
    bean.addConstructorArg(pattern);

    // 하지만 이는 선택적인 프로퍼티이다
    String lenient = element.getAttribute("lenient");
    if (StringUtils.hasText(lenient)) {
       bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
    }
  }
}
  1. 하나의 BeanDefinition을 생성하는 다수의 기본 작업을 처리할 때 스프링이 제공하는 AbstractSingleBeanDefinitionParser를 사용한다.
  2. 단일 BeanDefinition으로 표현할 타입의 AbstractSingleBeanDefinitionParser 슈퍼클래스를 제공한다.

이 간단한 예시에서는 지금 설명한 내용이 해야 할 일 전부이다. 단일 BeanDefinition의 생성은 빈 정의의 유일한 식별자를 설정하고 추출해서 AbstractSingleBeanDefinitionParser 슈퍼클래스가 처리한다.

D.5 핸들러와 스키마 등록

코딩은 끝났다! 남은 작업은 여기서 사용한 커스텀 요소를 이해하는 스프링 XML 파싱 인트라스트럭처를 만드는 것 뿐이다. 두 가지 특수한 목적의 프로퍼티 파일에 커스텀 namespaceHandler와 커스텀 XSD 파일을 등록해서 파싱 인트라스트럭처를 만든다. 이 프로퍼티 파일은 모두 애플리케이션의 'META-INF' 디렉터리에 두고 JAR 파일의 바이너리 클래스 옆에 같이 배포할 수도 있다. 스프링 XML 파싱 인프라스트럭처는 이 특수한 프로퍼티 파일을 사용해서 자동으로 새로운 확장(extension)을 선택할 것이다. 이 프로퍼티 파일의 형식은 다음과 같다.

D.5.1 'META-INF/spring.handlers'

'spring.handlers' 프로퍼티 파일에는 XML Schema URI와 네임스페이스 핸들러 클래스의 매핑이 포함되어 있다. 이 예제에서는 다음과 같이 작성해야 한다.

http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler

(':' 문자는 Java 프로퍼티 형식에서 유효한 구분자이므로 URI 내의 ':' 문자는 역슬래시로 이스케이프 해야 한다.)

키-값 쌍의 첫 부분(키 부분)은 커스텀 네임스페이스 확장(extension)와 연관된 URI이고 커스텀 XSD 스키마에서 지정했듯이 'targetNamespace' 속성의 값과 정확하게 일치해야 한다.

D.5.2 'META-INF/spring.schemas'

'spring.schemas' 프로퍼티 파일에는 XML Schema 위치('xsi:schemaLocation' 속성 일부분으로 스키마를 사용하는 XML 파일의 스키마 선언과 같이 참조되는)와 classpath 리소스의 매핑이 포함되어 있다. 이 파일은 스키마 파일을 가져오려고 인터넷 접근을 해야 하는 기본 EntityResolver를 스프링이 사용하는 것을 막아야 할 필요가 있다. 이 프로퍼티 파일에서 매핑을 지정한다면 스프링이 클래스패스에서 스키마를 검색할 것이다.(여기서는 'org.springframework.samples.xml' 패키지의 'myns.xsd')

http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd

이에 따라 클래스패스에 NamespaceHandler와 BeanDefinitionParser 클래스와 같이 XSD 파일을 배포해야 한다.

D.6 스프링 XML 구성에서 커스텀 확장(extension) 사용하기

직접 구현한 커스텀 확장을 사용하는 것은 스프링이 직접 제공하는 'custom' 확장을 사용한 것과 다르지 않다. 이전 단계에서 스프링 XML 구성 파일에 만든 커스텀 <dateformat/> 요소를 사용하는 예시가 다음에 나와 있다.


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:myns="http://www.mycompany.com/schema/myns"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mycompany.com/schema/myns http://www.mycompany.com/schema/myns/myns.xsd">

  
  <myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>

  <bean id="jobDetailTemplate" abstract="true">
    <property name="dateFormat">
       <!-- 내부(inner) 빈 -->
       <myns:dateformat pattern="HH:mm MM-dd-yyyy"/>
    </property>
  </bean>
</beans>


D.7 더 충실한 예제

커스텀 XML 확장에 대한 더 자세한 예제가 다음에 나와 있다.

D.7.1 커스텀 태그에 커스텀 태그 중첩하기

이 예제는 다음 구성의 대상을 만족하기 위해 필요한 다양한 아티팩트를 작성하는 방법을 설명한다.


<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:foo="http://www.foo.com/schema/component"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.foo.com/schema/component http://www.foo.com/schema/component/component.xsd">

  <foo:component id="bionic-family" name="Bionic-1">
    <foo:component name="Mother-1">
      <foo:component name="Karate-1"/>
      <foo:component name="Sport-1"/>
    </foo:component>
    <foo:component name="Rock-1"/>
  </foo:component>
</beans>

위 구성은 커스텀 확장을 서로의 내부에 실제로 넣는다. <foo:component/> 요소로 구성한 실제 클래스는 Component 클래스이다.(바로 아래 나온다.) Component 클래스가 'components' 프로퍼티에 setter 메서드를 노출하지 않는 방법을 봐야 한다. 이를 setter 주입으로 Component 클래스의 빈 정의를 구성하는 것은 상당히 어렵다.(거의 불가능에 가깝다.)

package com.foo;

import java.util.ArrayList;
import java.util.List;

public class Component {
  private String name;
  private List<Component> components = new ArrayList<Component> ();

  // 음, 'components'에는 setter 메서드가 없다
  public void addComponent(Component component) {
    this.components.add(component);
  }

  public List<Component> getComponents() {
    return components;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

이 이슈에 대한 일반적인 해결책은 'components' 프로퍼티에 setter 프로퍼티를 노출하는 커스텀 FactoryBean를 생성하는 것이다.

package com.foo;

import org.springframework.beans.factory.FactoryBean;

import java.util.List;

public class ComponentFactoryBean implements FactoryBean<Component> {
  private Component parent;
  private List<Component> children;

  public void setParent(Component parent) {
    this.parent = parent;
  }

  public void setChildren(List<Component> children) {
    this.children = children;
  }

  public Component getObject() throws Exception {
    if (this.children != null && this.children.size() > 0) {
       for (Component child : children) {
          this.parent.addComponent(child);
       }
    }
    return this.parent;
  }

  public Class<Component> getObjectType() {
    return Component.class;
  }

  public boolean isSingleton() {
    return true;
  }
}

이 예제는 잘 만들어졌고 동작도 잘 동작하지만, 스프링 기능을 최종 사용자에게 너무 많이 노출한다. 이러한 스프링의 기능은 모두 숨긴 채 커스텀 확장을 작성하는 것이 우리가 하려는 것이다. 앞에서 설명한 단계를 충실히 따랐다면 커스텀 태그의 구조를 정의하는 XSD 스키마를 생성하는 것에서 시작할 수 있다.



<xsd:schema xmlns="http://www.foo.com/schema/component"
       xmlns:xsd="http://www.w3.org/2001/XMLSchema"
       targetNamespace="http://www.foo.com/schema/component"
       elementFormDefault="qualified"
       attributeFormDefault="unqualified">

  <xsd:element name="component">
    <xsd:complexType>
       <xsd:choice minOccurs="0" maxOccurs="unbounded">
          <xsd:element ref="component"/>
       </xsd:choice>
       <xsd:attribute name="id" type="xsd:ID"/>
       <xsd:attribute name="name" use="required" type="xsd:string"/>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

그다음 커스텀 NamespaceHandler를 만든다.

package com.foo;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class ComponentNamespaceHandler extends NamespaceHandlerSupport {
  public void init() {
    registerBeanDefinitionParser("component", new ComponentBeanDefinitionParser());
  }
}

다음은 커스텀 BeanDefinitionParser의 차례이다. 여기서 만들고 있는 것이 ComponentFactoryBean를 설명하는 BeanDefinition이라는 것을 명심해라.

package com.foo;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;

import java.util.List;

public class ComponentBeanDefinitionParser extends AbstractBeanDefinitionParser {
  protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
    return parseComponentElement(element);
  }

  private static AbstractBeanDefinition parseComponentElement(Element element) {
    BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(ComponentFactoryBean.class);
    factory.addPropertyValue("parent", parseComponent(element));

    List<Element> childElements = DomUtils.getChildElementsByTagName(element, "component");
    if (childElements != null && childElements.size() > 0) {
       parseChildComponents(childElements, factory);
    }

    return factory.getBeanDefinition();
  }

  private static BeanDefinition parseComponent(Element element) {
    BeanDefinitionBuilder component = BeanDefinitionBuilder.rootBeanDefinition(Component.class);
    component.addPropertyValue("name", element.getAttribute("name"));
    return component.getBeanDefinition();
  }

  private static void parseChildComponents(List<Element> childElements, BeanDefinitionBuilder factory) {
    ManagedList<BeanDefinition> children = new ManagedList<BeanDefinition>(childElements.size());

    for (Element element : childElements) {
       children.add(parseComponentElement(element));
    }

    factory.addPropertyValue("children", children);
  }
}

마지막으로 스프링 XML 인프라스트럭쳐에 다양한 아티팩트를 등록해야 한다.

# in 'META-INF/spring.handlers'
http\://www.foo.com/schema/component=com.foo.ComponentNamespaceHandler
# in 'META-INF/spring.schemas'
http\://www.foo.com/schema/component/component.xsd=com/foo/component.xsd


D.7.2 'normal' 요소 상의 커스텀 속성

커스텀 파서와 관련 아티팩트를 작성하는 것이 어렵지는 않지만 때에 따라서는 좋은 방법이 아니다. 이미 존재하는 빈 정의에 메타데이터를 추가해야 하는 경우를 생각해 보자. 이럴 때 당연히 전체 커스텀 확장을 작성하는 것을 원치 않을 것이고 기존의 빈 정의 요소에 추가적인 요소를 더하기만을 원할 것이다.

다른 예시로 클러스터링 JCache에 접근하는(그렇다는 것을 알지는 못하는) 서비스 객체에 대한 빈 정의를 정의하는 서비스 클래스를 생각해 보자. 그리고 JCache라는 이름의 인스턴스가 사용하는 클러스터 내에서 eager 모드로 시작하는 것을 보장하기를 원한다.

<bean id="checkingAccountService" class="com.foo.DefaultCheckingAccountService"
    jcache:cache-name="checking.account">
  <!-- 그 외 의존성은 여기에 작성한다... -->
</bean>

'jcache:cache-name' 속성을 파싱했을 때 또 하나의 BeanDefinition을 생성하는 것이 여기서 하려는 것이다. 이 BeanDefinition가 이름을 가진 JCache를 초기화할 것이다. 여기서 'checkingAccountService'에 대한 기존의 BeanDefinition도 수정할 것이므로 새로운 JCache를 초기화하는 이 BeanDefinition에 의존성을 가질 것이다.

package com.foo;

public class JCacheInitializer {
  private String name;

  public JCacheInitializer(String name) {
    this.name = name;
  }

  public void initialize() {
    // 이름을 가진 캐시를 초기화하려고 다수의 JCache API를 호출한다...
  }
}

이제 커스텀 확장을 보자. 우선, 커스텀 속성을 나타내는 XSD 스키마을 장성한다.(이 경우 아주 쉽다.)



<xsd:schema xmlns="http://www.foo.com/schema/jcache"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      targetNamespace="http://www.foo.com/schema/jcache"
      elementFormDefault="qualified">

  <xsd:attribute name="cache-name" type="xsd:string"/>
</xsd:schema>

다음으로 관련된 NamespaceHandler를 작성한다.

package com.foo;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class JCacheNamespaceHandler extends NamespaceHandlerSupport {
  public void init() {
    super.registerBeanDefinitionDecoratorForAttribute("cache-name", new JCacheInitializingBeanDefinitionDecorator());
  }
}

이제 파서의 차례이다. 이 예제에서 XML 속성을 파싱할 예정이므로 BeanDefinitionParser 대신 BeanDefinitionDecorator를 작성한다.

package com.foo;

import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JCacheInitializingBeanDefinitionDecorator implements BeanDefinitionDecorator {
  private static final String[] EMPTY_STRING_ARRAY = new String[0];

  public BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder holder, ParserContext ctx) {
    String initializerBeanName = registerJCacheInitializer(source, ctx);
    createDependencyOnJCacheInitializer(holder, initializerBeanName);
    return holder;
  }

  private void createDependencyOnJCacheInitializer(BeanDefinitionHolder holder, String initializerBeanName) {
    AbstractBeanDefinition definition = ((AbstractBeanDefinition) holder.getBeanDefinition());
    String[] dependsOn = definition.getDependsOn();
    if (dependsOn == null) {
       dependsOn = new String[]{initializerBeanName};
    } else {
       List dependencies = new ArrayList(Arrays.asList(dependsOn));
       dependencies.add(initializerBeanName);
       dependsOn = (String[]) dependencies.toArray(EMPTY_STRING_ARRAY);
    }
    definition.setDependsOn(dependsOn);
  }

  private String registerJCacheInitializer(Node source, ParserContext ctx) {
    String cacheName = ((Attr) source).getValue();
    String beanName = cacheName + "-initializer";
    if (!ctx.getRegistry().containsBeanDefinition(beanName)) {
       BeanDefinitionBuilder initializer = BeanDefinitionBuilder.rootBeanDefinition(JCacheInitializer.class);
       initializer.addConstructorArg(cacheName);
       ctx.getRegistry().registerBeanDefinition(beanName, initializer.getBeanDefinition());
    }
    return beanName;
  }
}

마지막으로 스프링 XML 인프라스트럭처에 다양한 아티팩트를 등록해야 한다.

# in 'META-INF/spring.handlers'
http\://www.foo.com/schema/jcache=com.foo.JCacheNamespaceHandler
# in 'META-INF/spring.schemas'
http\://www.foo.com/schema/jcache/jcache.xsd=com/foo/jcache.xsd


D.8 추가 자료

이번 장에서 설명한 XML 스키마와 확장 가능한 XML 지원에 대한 추가 자료를 다음 링크에서 볼 수 있다.

2015/07/12 05:01 2015/07/12 05:01