SpringExt 源码学习

11 篇文章 0 订阅
9 篇文章 0 订阅
本文探讨了SpringExt如何增强Spring Schema的拓展性,通过定义和声明实现动态拓展功能。详细讲解了SpringExt的初始化流程,包括自定义xsd文件、schema映射、NamespaceHandler和BeanDefinitionParser的编写。此外,还分析了Webx如何重写XmlWebApplicationContext,并在初始化过程中整合SpringExt。重点介绍了SpringExtSchemaSet、SchemaEntityResolver和ConfigurationPointNamespaceHandlerResolver在Webx初始化中的作用。
摘要由CSDN通过智能技术生成

大致了解SpringExt

Spring Schema提供了我们便捷的初始化bean的方法,我们不需要再去写构造器注入或者属性注入直接使用类似的如下配置代码即可完成bean的初始化

<resource-loading id="resourceLoadingService"
              xmlns="http://www.alibaba.com/schema/services/resource-loading">
    <resource pattern="/file">
        <file-loader basedir="${user.home}" />
    </resource>
    <resource pattern="/webroot">
        <webapp-loader />
    </resource>
</resource-loading>

但是Spring Schema是不具有拓展性的,我们不能随意添加一个database-loader,对比以上代码

<resource-loading id="resourceLoadingService"
                  xmlns="http://www.alibaba.com/schema/services/resource-loading">
    <resource pattern="/file">
        <file-loader basedir="${user.home}" />
    </resource>
    <resource pattern="/webroot">
        <webapp-loader />
    </resource>
    <resource pattern="/db">
        <database-loader connection="jdbc:mysql:mydb" /> 
    </resource>
</resource-loading>

于是SpringExt帮助我们通过定义拓展点(ConfigurationPoint)和声明贡献(Contribution)的方式实现我们的动态拓展功能:

<resource-loading id="resourceLoadingService"
              xmlns="http://www.alibaba.com/schema/services"
              xmlns:loaders="http://www.alibaba.com/schema/services/resource-loading/loaders">
    <resource pattern="/file">
        <loaders:file-loader basedir="${user.home}" />
    </resource>
    <resource pattern="/webroot">
        <loaders:webapp-loader />
    </resource>
    <resource pattern="/db">
        <loaders:database-loader connection="jdbc:mysql:mydb" /> 
    </resource>
</resource-loading>

同时SpringExt提供将远程的xsd文件存储至本地的功能

以上的例子请详细阅读Webx的文档:http://www.openwebx.org/docs/Webx3_Guide_Book.html#webx.overview.springext

Spring IOC 初始化

spring在DefaultBeanDefinitionDocumentReader.parseBeanDefinitions方法中会根据当前的element的namespaceUri判断是否是Spring定义的还是用户自定义的

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)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

SpringExt就是借助自定义区进行解析

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    String namespaceUri = getNamespaceURI(ele);
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    }
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

实现自定义的基本流程

1.编写xsd文件

截取了一段

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<xsd:schema xmlns="http://www.springframework.org/schema/tool"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.springframework.org/schema/tool"
        elementFormDefault="qualified">

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

    ...

    <xsd:complexType name="registersScopeType">
        <xsd:attribute name="name" type="xsd:string" use="required">
            <xsd:annotation>
                <xsd:documentation><![CDATA[
    Defines the name of a custom bean scope that the annotated type registers, e.g. "conversation".
    Such a scope will be available in addition to the standard "singleton" and "prototype" scopes
    (plus "request", "session" and "globalSession" in a web application environment).
        ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>

</xsd:schema>

2.META-INF/spring.schemas 文件中建立namespace.xsd 与xsd 文件实际路径的映射

也就是类似于

http://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd

3.编写NamespaceHandler,并在META-INF/spring.handlers建立Namespace与NamespaceHandler的联系

http://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler

4.实现若干BeanDefinitionParser类,用于解析Xml元素,生成BeanDefinition。BeanDefinitionParser需要在NamespaceHandler中注册。

以上步骤也可以继承NamespaceHandlerSupport类,这个抽象类完成了NamespaceHandler的parse功能,也提供了注册BeanDefinitionParser的方法,是Webx使用的。

Webx的初始化

首先Webx重写了XmlWebApplicationContext。

public class XmlWebApplicationContext extends org.springframework.web.context.support.XmlWebApplicationContext
    implements ResourceLoadingExtendable 

同时实现了父类的initBeanDefinitionReader实现加入SpringExt相关的内容。一下这部分代码是主要的加载拓展点捐赠的过程

public void addConfigurationPointsSupport() {
    if (skipValidation) {
        reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE);
        reader.setNamespaceAware(true); // 为了添加Configuration Point支持,namespace是必须打开的。
        reader.setDocumentReaderClass(DocumentReaderSkippingValidation.class); // 对beans中的参数提供默认值

        log.warn(
                "XSD validation has been disabled according to the system property: -D{}.  Please be warned: NEVER skipping validation in Production Environment.",
                PROPERTY_SKIP_VALIDATION);
    }

    ResourceLoader resourceLoader = reader.getResourceLoader();

    if (resourceLoader == null) {
        resourceLoader = new DefaultResourceLoader();
    }

    ClassLoader classLoader = resourceLoader.getClassLoader();

    // schema providers
    SpringExtSchemaSet schemas = new SpringExtSchemaSet(classLoader);

    // default resolvers
    EntityResolver defaultEntityResolver = new ResourceEntityResolver(resourceLoader);
    NamespaceHandlerResolver defaultNamespaceHandlerResolver = new DefaultNamespaceHandlerResolver(classLoader);

    // new resolvers
    EntityResolver entityResolver = new SchemaEntityResolver(defaultEntityResolver, schemas);
    NamespaceHandlerResolver namespaceHandlerResolver = new ConfigurationPointNamespaceHandlerResolver(schemas.getConfigurationPoints(), defaultNamespaceHandlerResolver);

    reader.setEntityResolver(entityResolver);
    reader.setNamespaceHandlerResolver(namespaceHandlerResolver);
}

这部分有三个主要的步骤:

  1. 初始化的流程全部,包括注册拓展点捐赠:SpringExtSchemaSet schemas = new SpringExtSchemaSet(classLoader);

  2. 定义新的EntityResolverEntityResolver entityResolver = new SchemaEntityResolver(defaultEntityResolver, schemas);

  3. 同时定义新的NamespaceHandlerResolver并使其能够取得SpringExt的NamespaceHandler的代码NamespaceHandlerResolver namespaceHandlerResolver = new ConfigurationPointNamespaceHandlerResolver(schemas.getConfigurationPoints(), defaultNamespaceHandlerResolver);

SpringExtSchemaSet

别看只有一行构造方法,但是跟进去会发现别有洞天!

SchemaSet将一组Schemas整合在一起的集合。那么是哪些Schemas呢?

public SpringExtSchemaSet(ClassLoader classLoader) {
    this(new ConfigurationPointsImpl(classLoader), new SpringPluggableSchemas(classLoader));
}

ConfigurationPointsImpl负责SpringExt的拓展特性的加载,而SpringPluggableSchemas则是将Spring所支持的META-INF/spring.schemas中定义的schemas移到本地服务器。这两者都集成自Schemas接口,这个接口只有一个方法就是获得一个Map

SchemaEntityResolver

主要是根据systemId获得其Schema的InputStream的过程,可对比Spring的DTD或者XSD的读取方法

public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
    log.trace("Trying to locate XML entity {} as configuration points schema.", systemId);

    Schema schema = schemas.findSchema(systemId);

    if (schema == null) {
        if (defaultEntityResolver != null) {
            return defaultEntityResolver.resolveEntity(publicId, systemId);
        } else {
            return null;
        }
    }

    log.debug("Found XML schema for systemId {}: {}", systemId, schema);

    return new InputSource(schema.getInputStream());
}

ConfigurationPointNamespaceHandlerResolver

ConfigurationPointImpl继承NamespaceHandlerSupport,主要是根据Namespace获得ConfigurationPoint

public NamespaceHandler resolve(String namespaceUri) {
    ConfigurationPoint cp = cps.getConfigurationPointByNamespaceUri(namespaceUri);

    if (cp != null) {
        return cp.getNamespaceHandler();
    } else if (defaultResolver != null) {
        return defaultResolver.resolve(namespaceUri);
    } else {
        return null;
    }
}

NamespaceHandlerSupport已经实现parse方法,ConfigurationPoint包含了拓展点的所有捐赠,根据namespaceUri即可获得其具体的捐赠,也就是具体的BeanDefinitionParser

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值