Spring 可扩展XML配置机制实践

本文介绍了如何在Spring框架中实现XML配置的扩展,通过定义XML Schema、解析器和NamespaceHandler,创建自定义的XML标签,使得Spring能够解析并管理这些自定义组件。示例中展示了类似于dubbo的自定义标签过程,包括环境配置、每个步骤的详细操作以及最终在Spring配置文件中的应用。
摘要由CSDN通过智能技术生成

用过dubbo的同学应该很熟悉下面的配置:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="hello-world-app"  />

    <!-- 使用zk为注册中心暴露服务地址 -->
    <dubbo:registry address="zookeeper://127.0.0.1:2181" />

    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />

    <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />

    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />

</beans>

我们通过 dubbo:service 对外提供RPC服务,一切都司空见惯。但你有没有想过dubbo:service 到底是什么?Spring是怎么解析它并把它注入到容器中的?

本文着重介绍Spring Framework 基于Schema风格的XML扩展机制,通过Spring提供的xml扩展机制,我们可以在spring.xml中加入自己的标签,之后Spring会帮我们解析并纳入自己的管理范围内。

环境配置

JDK 1.7
Spring 4.3.3.RELEASE
Maven 3.3
IDEA 15

示例

通过学习 Spring Extensible XML ,自己写了一个类似dubbo 自定义标签的demo样例供大家学习,工程结构如下图所示:
这里写图片描述

最后,我们也可以在Spring配置文件引入我们自定义的标签了,如下:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:rpc="http://www.bytebeats.com/schema/rpc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.bytebeats.com/schema/rpc http://www.bytebeats.com/schema/rpc/rpc.xsd">

    <rpc:registry id="zk" protocol="zookeeper" address="127.0.0.1" />
    <rpc:protocol id="hessian" name="hessian" port="9001"/>

    <rpc:service id="rpcService" ref="helloService" interface="com.bytebeats.spring4.extension.service.IHelloService" timeout="5000" retries="1"></rpc:service>

    <rpc:ref id="accountService" interface="com.bytebeats.spring4.extension.service.IAccountService" retries="0" check="false" />

    <bean id="helloService" class="com.bytebeats.spring4.extension.service.impl.HelloServiceImpl" />

</beans>
1、定义XML schema

首先,我们需要定义一个xsd文件来声明XML标签元素,本文中为rpc.xsd,如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.bytebeats.com/schema/rpc"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://www.bytebeats.com/schema/rpc"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">

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

    <xsd:complexType name="abstractConfig">
        <xsd:choice minOccurs="0" maxOccurs="unbounded">
            <xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded" />
        </xsd:choice>
        <xsd:anyAttribute namespace="##other" processContents="lax" />
    </xsd:complexType>

    <xsd:element name="service">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="abstractConfig">
                    <xsd:choice minOccurs="0" maxOccurs="unbounded">
                        <xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded" />
                    </xsd:choice>
                    <xsd:attribute name="id" type="xsd:ID" />
                    <xsd:attribute name="ref" type="xsd:string" use="required"/>
                    <xsd:attribute name="interface" type="xsd:string" use="required"/>
                    <xsd:attribute name="group" type="xsd:string" use="optional"/>
                    <xsd:attribute name="registry" type="xsd:string" use="optional"/>
                    <xsd:attribute name="version" type="xsd:string" use="optional"/>
                    <xsd:attribute name="timeout" type="xsd:string" use="optional"/>
                    <xsd:attribute name="retries" type="xsd:string" use="optional"/>
                    <xsd:attribute name="async" type="xsd:boolean" use="optional"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="ref">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="abstractConfig">
                    <xsd:choice minOccurs="0" maxOccurs="unbounded">
                        <xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded" />
                    </xsd:choice>
                    <xsd:attribute name="id" type="xsd:ID" />
                    <xsd:attribute name="interface" type="xsd:string" use="required"/>
                    <xsd:attribute name="group" type="xsd:string" use="optional"/>
                    <xsd:attribute name="registry" type="xsd:string" use="optional"/>
                    <xsd:attribute name="version" type="xsd:string" use="optional"/>
                    <xsd:attribute name="timeout" type="xsd:string" use="optional"/>
                    <xsd:attribute name="retries" type="xsd:string" use="optional"/>
                    <xsd:attribute name="async" type="xsd:boolean" use="optional"/>
                    <xsd:attribute name="check" type="xsd:boolean" use="optional"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="registry">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="abstractConfig">
                    <xsd:choice minOccurs="0" maxOccurs="unbounded">
                        <xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded" />
                    </xsd:choice>
                    <xsd:attribute name="id" type="xsd:ID" />
                    <xsd:attribute name="protocol" type="xsd:string" use="required"/>
                    <xsd:attribute name="address" type="xsd:string" use="required"/>
                    <xsd:attribute name="username" type="xsd:string" use="optional"/>
                    <xsd:attribute name="password" type="xsd:string" use="optional"/>
                    <xsd:attribute name="check" type="xsd:boolean" use="optional"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

    <xsd:element name="protocol">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="abstractConfig">
                    <xsd:choice minOccurs="0" maxOccurs="unbounded">
                        <xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded" />
                    </xsd:choice>
                    <xsd:attribute name="id" type="xsd:ID" />
                    <xsd:attribute name="name" type="xsd:string" use="required"/>
                    <xsd:attribute name="port" type="xsd:string" use="required"/>
                    <xsd:attribute name="host" type="xsd:string" use="optional"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

关于XML Schema这里不详述了,大家可以参考 w3school XML Schema 简介

2、定义解析器

定义一个BeanDefinitionParser负责解析xml,如下:

package com.bytebeats.spring4.extension.xml;

import com.bytebeats.spring4.extension.domain.RpcServiceBean;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 * ${DESCRIPTION}
 *
 * @author Ricky Fung
 * @create 2016-11-23 11:50
 */
public class RpcServiceBeanDefinitionParser extends AbstractBeanDefinitionParser {

    @Override
    protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {

        return parseComponet(element, parserContext);
    }

    private AbstractBeanDefinition parseComponet(Element element, ParserContext parserContext) {

        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(RpcServiceBean.class);

        String id = element.getAttribute("id");
        if (StringUtils.hasText(id)) {
            builder.addPropertyValue("id", id);
        }

        String ref = element.getAttribute("ref");
        builder.addPropertyValue("ref", ref);

        String interfaceName = element.getAttribute("interface");
        builder.addPropertyValue("interfaceName", interfaceName);

        String group = element.getAttribute("group");
        if (StringUtils.hasText(group)) {
            builder.addPropertyValue("group", group);
        }

        String registry = element.getAttribute("registry");
        if (StringUtils.hasText(registry)) {
            builder.addPropertyValue("registry", registry);
        }

        String version = element.getAttribute("version");
        if (StringUtils.hasText(version)) {
            builder.addPropertyValue("version", version);
        }

        String timeout = element.getAttribute("timeout");
        if (StringUtils.hasText(timeout)) {
            builder.addPropertyValue("timeout", Integer.parseInt(timeout));
        }

        String retries = element.getAttribute("retries");
        if (StringUtils.hasText(retries)) {
            builder.addPropertyValue("retries", Integer.parseInt(retries));
        }

        String async = element.getAttribute("async");
        if (StringUtils.hasText(async)) {
            builder.addPropertyValue("async", Boolean.valueOf(async));
        }

        return builder.getBeanDefinition();
    }

}
3、定义NamespaceHandler
package com.bytebeats.spring4.extension.xml;

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

/**
 * ${DESCRIPTION}
 *
 * @author Ricky Fung
 * @create 2016-11-23 11:48
 */
public class RpcNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("service", new RpcServiceBeanDefinitionParser());
        registerBeanDefinitionParser("ref", new RpcReferenceBeanDefinitionParser());
        registerBeanDefinitionParser("registry", new RpcRegistryBeanDefinitionParser());
        registerBeanDefinitionParser("protocol", new RpcProtocolBeanDefinitionParser());
    }
}
4、配置schema和handler

在META-INF目录下面分别新建spring.handlers和spring.schemas文件。

spring.schemas

http\://www.bytebeats.com/schema/rpc/rpc.xsd=/META-INF/rpc.xsd

spring.handlers

http\://www.bytebeats.com/schema/rpc=com.bytebeats.spring4.extension.xml.RpcNamespaceHandler
5、使用

辛苦这么大半天,接下来该看看成果了,首先是

applicationContext.xml

<?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:context="http://www.springframework.org/schema/context"
       xmlns:rpc="http://www.bytebeats.com/schema/rpc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.bytebeats.com/schema/rpc http://www.bytebeats.com/schema/rpc/rpc.xsd">

    <context:annotation-config/>
    <context:component-scan base-package="com.bytebeats.spring4.extension.xml"/>

    <rpc:registry id="zk" protocol="zookeeper" address="127.0.0.1" />
    <rpc:protocol id="hessian" name="hessian" port="9001"/>

    <rpc:service id="rpcService" ref="helloService" interface="com.bytebeats.spring4.extension.service.IHelloService" timeout="5000" retries="1"></rpc:service>

    <rpc:ref id="accountService" interface="com.bytebeats.spring4.extension.service.IAccountService" retries="0" check="false" />

    <bean id="helloService" class="com.bytebeats.spring4.extension.service.impl.HelloServiceImpl" />

</beans>

只需要加入 我们自定义的命名空间 即可使用了,如下图:
这里写图片描述


从Spring 容器中获取自定义的xml标签元素,如下:

public class App {

    public static void main( String[] args ) {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");

        IHelloService helloService = (IHelloService) context.getBean("helloService");
        System.out.println(helloService.sayHello("ricky"));

        RpcServiceBean rpcServiceBean = (RpcServiceBean) context.getBean("rpcService");
        System.out.println("rpcServiceBean:"+rpcServiceBean.getInterfaceName());

        RpcReferenceBean accountService = (RpcReferenceBean) context.getBean("accountService");
        System.out.println("accountService:"+accountService.getInterfaceName());

        RpcRegistryBean rpcRegistryBean = (RpcRegistryBean) context.getBean("zk");
        System.out.println("rpcRegistryBean:"+rpcRegistryBean.getAddress());

        RpcProtocolBean rpcProtocolBean = (RpcProtocolBean) context.getBean("hessian");
        System.out.println("rpcProtocolBean:"+rpcProtocolBean.getPort());

        context.close();
    }
}

点此下载源码https://github.com/TiFG/spring4-samples/tree/master/spring-ch3-extensible

参考

Spring Extensible XML:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/xml-custom.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值