spring拓展之定义自己的namespace

  • Post author:
  • Post category:其他


1.查看源码认识spring是怎么加载xml配置的

1.1 spring是怎么创建对象的?

查看spring beanFactory的继承关系

这里写图片描述

通过查看源码可以得知,BeanFactory 中的对象创建是实际是根据RootBeanDefinition创建的,

在AbstractAutowireCapableBeanFactory中有具体的实现,包括创建实例,利用Spring拓展

java的内省实现BeanWrapperImpl,创建对象的包装类,使用反射给对象填充属性,并实现依赖注入DI

。具体可以自行参阅源码。

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    .....
    protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
            throws BeanCreationException;

}

这里写图片描述

而RootBeanDefination定义的是什么呢?查看AbstractBeanDefination类。

这里写图片描述

可以看到这里就是Spring对对象属性的封装,包括类名,属性,加载策略等等,其实也就是我们在xml里

配置的对象。

1.2spring是怎么将xml里配置的对象读到BeanFactory中的?

在查看spring容器的源码时,得知spring 是使用

org.springframework.beans.factory.xml.XmlBeanDefinitionReader 进行xml解析的

    public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
        .....

        protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                    throws BeanDefinitionStoreException {
                try {
                    //读取xml
                    Document doc = doLoadDocument(inputSource, resource);
                    //解析并注册xml中定义的BeanDefination
                    return registerBeanDefinitions(doc, resource);
                }
                catch (BeanDefinitionStoreException ex) {
                    xxx
                }
            }
        .....
    }

接下来查看对dom 解析部分的源码

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
@Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }
    protected void doRegisterBeanDefinitions(Element root) {
        //为了实现进行递归解析
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
            //对profile的支持
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }

        preProcessXml(root);
        //开始解析dom树
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
            NodeList nl = root.getChildNodes();
            for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element) {
                    Element ele = (Element) node;
                    if (delegate.isDefaultNamespace(ele)) {
                        //spring的基础命名元素解析(import、bean、beans、alias)其中在
                        //beans嵌套时会进行递归解析
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //拓展元素解析
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
}

查看spring是怎么实现拓展元素的解析的

public class BeanDefinitionParserDelegate {
    public BeanDefinition parseCustomElement(Element ele) {
        return parseCustomElement(ele, null);
    }

    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        //获取当前节点命名空间URI
        String namespaceUri = getNamespaceURI(ele);
        //根据命名空间解析到自定义的NamespaceHandler
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        //使用拓展的Handler对当前节点进行解析
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
}

其中NamespaceHandlerResolver 会去查找当前项目中classpath 下的META-INF目录下所有文件名为

spring.handlers配置文件,定位到自定义namespace的解析器实现类。其中在namespace 的处理

器中可以通过进行BeanDefination的注册,注册过的BeanDefination会用来给BeanFactory创建对象

使用,将解析好的BeanDefination注册到parserContext.getRegistry()中即可。其实DefaultListableBeanFactory

就是一个BeanDefinitionRegistry。

@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        ....
        parserContext.getRegistry().registerBeanDefinition(beanName, mbd);
    }

2.定义自己的namespace

2.1 定义schema约束xsd文件

将自定义的xsd文件放到项目的 META-INF 目录下。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://xxx.xxx.com/schema/myns"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:tool="http://www.springframework.org/schema/tool"
    targetNamespace="http://xxx.xxx.com/schema/myns">

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

    <xsd:annotation>
        <xsd:documentation><![CDATA[ Namespace support for the myns test. ]]></xsd:documentation>
    </xsd:annotation>

    <xsd:complexType name="mybeanType">
        <xsd:attribute name="id" type="xsd:ID">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="name" type="xsd:string" use="required">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The mybean name. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="class" type="xsd:string" use="required">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The version. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>


    <xsd:element name="mybean" type="mybeanType">
        <xsd:annotation> 
            <xsd:documentation><![CDATA[ The mybean config ]]></xsd:documentation> 
        </xsd:annotation>
    </xsd:element>


</xsd:schema>

更多xsd写法可以参阅xml的相关资料。

2.2创建自定义namespace的NamespaceHandler

使用NamespaceHandlerSupport来实现我们定义的NamespaceHandler。在init时去提供具体的标签的

解析器。

BeanDefinitionParser

public class MybeanParser implements BeanDefinitionParser {

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {

        RootBeanDefinition mbd =  new RootBeanDefinition();
        mbd.setBeanClassName(element.getAttribute("class"));
        String beanName = element.getAttribute("id");
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        mutablePropertyValues.add("name", element.getAttribute("name"));
        mbd.setPropertyValues(mutablePropertyValues);
        parserContext.getRegistry().registerBeanDefinition(beanName, mbd);

        return mbd;
    }

}

实现自定义的NamespaceHandler

public class MynsNameSpaceHandler extends NamespaceHandlerSupport{
    @Override
    public void init() {
        registerBeanDefinitionParser("mybean", new MybeanParser());
    }
}

这里的mybean是myns namespace下的元素标签的具体的解析实现

如:

<myns:mybean></myns:mybean>

2.3配置自定义的NamespaceHandler映射

在META-INF下创建文件 spring.handlers

http\://xxx.xxx.com/schema/myns=com.xxx.MynsNameSpaceHandler

2.4使用自定义的Namespace

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:myns="http://xxx.xxx.com/schema/myns"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://xxx.xxx.com/schema/myns http://xxx.xxx.com/schema/myns.xsd
        ">

        <myns:mybean class="com.xxx.People" id="mybean123" name="testMybean"></myns:mybean>

</beans>

2.5测试

public class MybeanNamespaceTestCase {

    @SuppressWarnings("resource")
    @Test
    public void testGetBean(){

        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"myapp.xml"},false);
        ac.setValidating(false);
        ac.refresh();

        People bean = ac.getBean("mybean123",People.class);

        System.out.println(bean.getName());

    }

}

这里设置ac.setValidating(false); 是因为如果开启xml约束检查,需要配置schema的位置,

也是在META-INF 下新建spring.schemas

并加入:

http\://xxx.xxx.com/schema/myns.xsd=META-INF/myns.xsd

这样spring 在xml解析时会调用org.springframework.beans.factory.xml.PluggableSchemaResolver

进行获取schema文件进行约束检查,

这样配置完毕后就可以ac.setValidating(true);啦,如果文件内容不符合规范,会启动时抛出异常。

此外,如果想要在使用过程开发工具能够像使用spring 自身的一些配置时有提升功能,可以将schema文件

上传到文件服务器上,能够通过http 访问到xsi:schemaLocation的地方,或者配置开发工具中的xml 约束映射,将地址映射到本

地磁盘中,这样就能

这里写图片描述

这里写图片描述



版权声明:本文为m0_38043362原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。