Java
spring

Spring Framework Bean定義のXML:カスタムタグ

はじめに

Spring FrameworkのドキュメントでBean定義のXML要素"<util:list>"の例がある。
https://docs.spring.io/spring/docs/5.0.x/spring-framework-reference/core.html#xsd-schemas-util-list

<!-- creates a java.util.List instance with the supplied values -->
<util:list id="emails">
    <value>pechorin@hero.org</value>
    <value>raskolnikov@slums.org</value>
    <value>stavrogin@gov.org</value>
    <value>porfiry@gov.org</value>
</util:list>

このコードの意味は下と同じ。

<!-- creates a java.util.List instance with values loaded from the supplied 'sourceList' -->
<bean id="emails" class="org.springframework.beans.factory.config.ListFactoryBean">
    <property name="sourceList">
        <list>
            <value>pechorin@hero.org</value>
            <value>raskolnikov@slums.org</value>
            <value>stavrogin@gov.org</value>
            <value>porfiry@gov.org</value>
        </list>
    </property>
</bean>

どんな魔法をかけるのかちょっと興味があるので、ソースコードに探し始めた。

魔法の真実

まず、Eclipseでインタフェースorg.springframework.beans.factory.xmlBean.DefinitionParserを実装するクラスをさがす。いっばいのクラスでてきた。その中org.springframework.beans.factory.xml.UtilNamespaceHandler(ジャバアーカイブspring-beans-3.2.3.RELEASE.jar)があやしい。ソースコードを読み、

org.springframework.beans.factory.xml.UtilNamespaceHandler
...
    public void init() {
        registerBeanDefinitionParser("constant", new ConstantBeanDefinitionParser());
        registerBeanDefinitionParser("property-path", new PropertyPathBeanDefinitionParser());
        registerBeanDefinitionParser("list", new ListBeanDefinitionParser());
        registerBeanDefinitionParser("set", new SetBeanDefinitionParser());
        registerBeanDefinitionParser("map", new MapBeanDefinitionParser());
        registerBeanDefinitionParser("properties", new PropertiesBeanDefinitionParser());
    }
...

XML要素"list"の扱うクラスを見つけるかな。同じのジャバアーカイブの中、ファイル"/META-INF/spring.handlers"を確認してみると、

http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler

ビンゴ!名前空間"util"の要素は本当にクラスorg.springframework.beans.factory.xml.UtilNamespaceHandlerに扱う。メソッドinit()に戻りましょ。XML要素"list"はクラスListBeanDefinitionParser(org.springframework.beans.factory.xml.UtilNamespaceHandlerのインナークラス)に扱う。

org.springframework.beans.factory.xml.UtilNamespaceHandler
...
    private static class ListBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

        @Override
        protected Class getBeanClass(Element element) {
            return ListFactoryBean.class;
        }

        @Override
        protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
            String listClass = element.getAttribute("list-class");
            List parsedList = parserContext.getDelegate().parseListElement(element, builder.getRawBeanDefinition());
            builder.addPropertyValue("sourceList", parsedList);
            if (StringUtils.hasText(listClass)) {
                builder.addPropertyValue("targetListClass", listClass);
            }
            String scope = element.getAttribute(SCOPE_ATTRIBUTE);
            if (StringUtils.hasLength(scope)) {
                builder.setScope(scope);
            }
        }
    }
...

メソッドgetBeanClass()はファクトリクラスorg.springframework.beans.factory.config.ListFactoryBeanを設定する。そしてメソッドdoParse()はXML属性と子要素をよむ役目。

参考資料

https://docs.spring.io/spring/docs/5.0.x/spring-framework-reference/core.html#xml-custom