2.0버전 이후, Spring은 bean을 정의하고 설정하기 위한 기본적인 Spring XML포맷에 대해 스키마-기반의 확장을 위한 기법을 제공한다. 이 부분은 자체적인 사용자정의 bean정의 파서를 작성하고 Spring IoC컨테이너로 파서를 통합하는 방법을 다룬다.
스키마를 인식하는 XML편집기를 사용하여 설정파일 작업을 돕기 위해, Spring의 확장가능한 XML설정 기법은 XML스키마에 기초를 둔다. Spring의 최근 XML설정 확장에 친숙하지 않다면 Appendix A, XML 스키마-기반 설정를 먼저보라.
다음의 간단한 XML스키마 작성 절차로 새로운 XML설정 확장을 생성할수 있다. 새로운 XML설정 확장을 생성하는 것은 다음 XML스키마의 간단한 처리를 통해 가능하다. NamespaceHandler 구현물을 코딩하고, 전용 프라퍼티 파일에 하나 이상의 BeanDefinitionParser 인스턴스를 코딩하고 NamespaceHandler와 스키마를 등록한다. 다음은 이러한 단계의 각각에 대한 상세설명이다. 예제에서, 우리는 Spring IoC컨테이너내 SimpleDateFormat타입의 객체를 직접 설정하는 것을 허용하는 XML확장(사용자정의 XML요소)을 생성할것이다.
Spring의 IoC컨테이너를 사용하기 위한 XML설정 확장을 생성하는 것은 확장을 설명하는 XML스키마를 작성하는 것을 시작한다. 다음은 우리가 SimpleDateFormat 객체를 설정하기 위해 사용할 스키마이다. 강조된 줄은 확인될(컨테이너에서 bean 식별자로 사용될 id 속성을 가진다는 것을 의미한다.) 모든 태그를 위한 확장의 기본사항을 포함한다.
#### myns.xsd (inside package org/springframework/samples/xml)
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/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>
위 스키마는 myns:dateformat 설정 지시를 사용하여 XML 애플리케이션 컨텍스트 파일내 직접 SimpleDateFormat 객체를 설정하기 위해 사용될것이다. 위에서 노트된것처럼, id 속성은(이 경우) SimpleDateFormat bean을 위한 bean식별자로 사용된다.
<myns:dateformat id="dateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>
우리가 내불구조를 나타내는 클래스를 생성한 뒤에, XML의 위 일부분은 다음의 XML일부와 정확하게 같을것이다. 반면에, 우리는 컨테이너에 프라퍼티 세트의 쌍을 가지고 SimpleDateFormat 타입의 dateFormat으로 식별되는 bean을 생성한다.
<bean id="dateFormat" class="java.text.SimpleDateFormat"> <constructor-arg value="yyyy-HH-dd HH:mm"/> <property name="lenient" value="true"/> </bean>
![]() | Note |
---|---|
설정 포맷을 생성하기 위한 스키마-기반의 접근법은 스키마를 인지하는 XML편집기를 가진 IDE와 통합할수 있다. 작성된 스키마를 사용하여, 예들 들어 당신은 반복내 정의된 여러개의 설정 옵션들간의 사용자선택을 하기 위해 자동완성을 사용할수 있다. |
스키마에 추가적으로, 우리는 Spring이 설정파일을 파싱하는 동안 만나게 되는 이 특정 명명공간의 모든 요소를 파싱하는 NamespaceHandler가 필요하다. 우리의 경우 NamespaceHandler는 myns:dateformat 요소를 파싱하는 것을 관리한다.
NamespaceHandler 인터페이스는 오직 3가지 메소드 기능을 가진다는 면에서 매우 간단하다.
init() - NamespaceHandler의 초기화와 핸들러가 사용되기 전 Spring에 의해 호출될것이다.
BeanDefinition parse(Element element, ParserContext parserContext) - Spring이 가장 상위레벨(bean정의내 내포되거나 다른 명명공간이 아닌)의 요소를 만날때 호출된다. 이 메소드는 bean정의를 등록하고/하거나 bean정의를 반환할수 있다.
BeanDefinitionHolder decorate(Node element, BeanDefinitionHolder definition, ParserContext parserContext) - Spring이 다른 명명공간(예를 들어 Spring명명공간내부)의 속성이나 내포된 요소를 만날때 호출된다. 하나 이상의 bean정의의 장식(decoration)은 Spring 2.0에 포함된 특별한 범위(scope - 범위에 대한 좀더 많은 정보를 위해서는 Section 3.5, “Bean scopes”를 보라.)에서 사용된다. 우리는 간단한 예제를 강조하여 시작할것이다.
전체 명명공간을 위해 자체적인 NamespaceHandler를 코딩하는 것이 완전히 가능하다 하더라도, 이것은 종종 Spring XML설정파일내 각각의 가장 상위레벨의 XML요소는 하나의 bean정의의 결과가 된다. (우리의 경우, myns:dateformat 요소가 있는 곳은 SimpleDateFormat bean 정의의 결과가 된다). Spring은 이러한 시나리오를 지원하는 2개의 편리한 클래스를 제공한다. 이 예제에서, 우리는 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());
}
}
볼수 있는것처럼, 위에서 보여지는 명명공간 핸들러는 BeanDefinitionParsers라고 불리는 것을 등록한다. 이 경우 BeanDefinitionParsers는 Spring 명명공간 핸들러가 이 특정 bean정의 파서에 맵핑되는 타입(이 경우 dateformat)의 XML요소를 만난다면 고려될것이다. 반면에, BeanDefinitionParser는 스키마내 정의된 가장 상위레벨 구별되는 XML요소를 파싱하기 위한 책임이 있다. 파서에서, 우리는 XML요소(그리고 하위요소)와 ParserContext에 접근할것이다. 후자는 아래 예제에서 수행한것처럼 예들 들어 BeanDefinitionRegistry에 대한 참조를 얻기 위해 사용될수 있다.
package org.springframework.samples.xml; import java.text.SimpleDateFormat; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.util.StringUtils; import org.w3c.dom.Element; public class SimpleDateFormatBeanDefinitionParser implements BeanDefinitionParser { public BeanDefinition parse(Element element, ParserContext parserContext) { // create a RootBeanDefinition that will serve as configuration // holder for the 'pattern' attribute and the 'lenient' attribute RootBeanDefinition beanDef = new RootBeanDefinition(); beanDef.setBeanClass(SimpleDateFormat.class); // never null since the schema requires it String pattern = element.getAttribute("pattern"); beanDef.getConstructorArgumentValues().addGenericArgumentValue(pattern); String lenientString = element.getAttribute("lenient"); if (StringUtils.hasText(lenientString)) { // won't throw exception if validation is turned on (boolean type set in schema) beanDef.getPropertyValues().addPropertyValue("lenient", new Boolean(lenientString)); } // retrieve the ID attribute that will serve as the bean identifier in the context String id = element.getAttribute("id"); // create a bean definition holder to be able to register the // bean definition with the bean definition registry // (obtained through the ParserContext) BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDef, id); // register the BeanDefinitionHolder (which contains the bean definition) // with the BeanDefinitionRegistry BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry()); return beanDef; } }
![]() | Note |
---|---|
이 예제에서, 우리는 BeanDefinition를 정의하고 이것을 BeanDefinitionRegistry에 등록한다. 당신은 등록기로 bean정의를 등록하거나 parse() 메소드로부터 bean정의를 반환할필요가 없다. 당신에게 주어진 정보와 ParserContext에서 당신이 원하는 것이 무엇이든 수행하는것에는 자유롭다. |
ParserContext 는 다음의 프라퍼티에 접근한다.
readerContext - bean factory와 내포된 명명공간을 분석하기 위해 선택적으로 사용될수 있는 NamespaceHandlerResolver에 접근한다.
parserDelegate - 설정파일을 파싱하는 컴포넌트를 제어. 대개 당신은 이것에 접근할 필요가 없다.
registry - 새롭게 생성된 BeanDefinition인스턴스를 등록하는 것을 허용하는 BeanDefinitionRegistry
nested - 처리된 XML요소가 외부(outer) bean정의인지 아닌지 표시(반면에, 이것은 전통적인 내부(inner) bean과 유사하게 정의된다.)
우리는 사용자정의 XML스키마를 파싱하는 것을 다룰 NamespaceHandler 와 BeanDefinitionParser를 구현한다. 우리는 다음의 결과물을 가진다.
org.springframework.samples.xml.MyNamespaceHandler - 하나 이상의 BeanDefinitionParser 인스턴스를 등록할 명명공간 핸들러
org.springframework.samples.xml.SimpleDateFormatBeanDefinitionParser - dateformat 타입의 요소를 파싱하기 위한 사용된 명명공간 핸들러
org/springframework/samples/xml/myns.xsd - Spring설정파일내 사용될 실제 스키마(이 파일은 우리가 나중에 볼 명명공간 핸들러와 파서 클래스와 나란히, 클래스패스에 둘 필요가 있다. )
우리가 마지막으로 해야할 필요가 있는 것은 두개의 특별한 목적을 가진 프라퍼티 파일에 이것을 등록하여 사용할 준비가 된 명명공간을 얻는것이다. 이러한 프라퍼티 파일은 META-INF에 두거나 JAR파일내 바이너리 클래스와 나란히 배포될수 있다. 일단 클래스패스에서 프라퍼티 파일을 찾으면 Spring은 새로운 명명공간과 핸들러를 자동으로 가질것이다.
spring.handlers라고 불리는 프라퍼티 파일은 명명공간 핸들러 클래스에 대한 XML스키마 URI의 맵핑을 포함한다. 예제에서, 우리는 다음을 언급할 필요가 있다.
http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler
spring.schemas라고 불리는 프라퍼티 파일은 클래스패스 자원에 대한 XML스키마 위치의 맵핑을 포함한다(xsi:schemaLocation 속성의 부분처럼 스키마를 사용하는 XML파일내 스키마 선언과 나란히 언급된). 이 파일은 Spring을 스키마 파일을 가져오기 위해 인터넷 접속을 요구하는 디폴트 EntityResolver 를 사용하는 것으로부터 막을필요가 있다. 당신이 프라퍼티 파일내 맵핑을 언급한다면, Spring은 스키마(이 경우 'org.springframework.samples.xml'패키지의 'myns.xsd')를 위해 클래스패스를 검색할것이다.
http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd