欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring源码解析之自定义标签的解析与Bean的注册

程序员文章站 2024-02-27 12:32:15
...

前面我们降解了Spring中默认标签的解析,下面我们介绍Bean的自定义标签的解析,为何Spring这么强大,就是它具有强大的可扩展性,因此我们的自定义标签也可以通过Spring解析,比如Dubbo的自定义标签,其解析也是委托给BeanDefinitionParserDelegate完成的,调用的方法为:parseCustomElement(Element ele),源码如下所示:

public BeanDefinition parseCustomElement(Element ele) {
    return parseCustomElement(ele, null);
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
    //获取命名空间
    String namespaceUri = getNamespaceURI(ele);
    //根据命名空间获取一个
    NamespaceHandler handler =  this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        return null;
    }
    //通过NamespaceHandler解析标签
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

通过上面的代码自定义标签的解析,是通过一个NamespaceHandler实例完成的,首先我们先看NamespaceHandler的定义如下:

public interface NamespaceHandler {
    //初始化
    void init();
    //解析元素返回一个BeanDefinition
    BeanDefinition parse(Element element, ParserContext parserContext);
    //解析元素返回一个BeanDefinitionHolder 
    BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);

NamespaceHandler解析标签,并且将标签返回一个Beandefinition实例或者BeanDefinitionHolder实例。如下为Spring提供的自定义标签解析的NamespaceHandler实现:

Spring源码解析之自定义标签的解析与Bean的注册

NamespaceHandler的获取实在DefaultNamespaceHandlerResolver完成的,它类似于SPI机制,它从META-INF/spring.handlers加载所有的Handlers,并且实例化,缓存在Map中。如下为spring-beans中的配置

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

 如果我们想实现自己的NamespaceHandler只需要继承NamespaceHandlerSupport即可,这样更加简单而只需实现init方法即可,在init方法中初始化我们的自定义标签解析器。如下为Spring提供的ContextNamespaceHandler处理context 命名空间:

public class ContextNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
	registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
	registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
	registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
	registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
	registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }
}

这里我们不再详解,感兴趣的可以自己阅读源码,为了加深对自定义标签解析的理解,下面我们编写一个解析自定义标签的例子。这里我们先介绍标签解析器BeanDefinitionParser的使用,用于解析Spring配置中的标签,如果要解析自定义标签,需要我们根据情况实现一些方法,如下为BeanDefinitionParser的一些实现,我们只需要根据自己的需要继承一下方法即可:

Spring源码解析之自定义标签的解析与Bean的注册

首先我们需要定义XML规范文件,即xsd文件,如果有对xsd不了解的可以自行百度,本人也是一瓶子不满、半瓶子晃荡,直接抄Spring官方文档的定义其,定义如下:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.baidu.com/schema/myns"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:beans="http://www.springframework.org/schema/beans"
    targetNamespace="http://www.baidu.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>

然后需要在META-INF目录的spring.schemas文件配置上面编写的xsd文件,示例如下:

http\://www.baidu.com/schema/myns/myns.xsd=cn/org/microservice/spring/ioc/customized/myns.xsd

然后需要编写BeanDefinitionParse标签解析器,这里我们继承类AbstractSingleBeanDefinitionParser代码如下:

public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    //返回标签要解析成的Bean的Class实例
    protected Class getBeanClass(Element element) {
        return SimpleDateFormat.class;
    }
    //解析元素
    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        // this will never be null since the schema explicitly requires that a
        // value be supplied
        String pattern = element.getAttribute("pattern");
        bean.addConstructorArg(pattern);
        // this however is an optional property
        String lenient = element.getAttribute("lenient");
        if (StringUtils.hasText(lenient)) {
            bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
        }
    }
}

之后,我们需要将前面编写的自定义的BeanDefinitionParse注册到Spring中,因此我们需要创建自定义NamespaceHandler,代码如下所示:

public class MyNamespaceHandler extends NamespaceHandlerSupport {
    public void init() {
        registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
    }
}

编写之后需要将MyNamespaceHandler 配置到META-INF目录的spring.handlers文件中,配置如下:

http\://www.baidu.com/schema/myns=cn.org.microservice.spring.ioc.customized.MyNamespaceHandler

到此为止,我们的自定义标签解析的工作已经完成了,下面我们使用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:myns="http://www.baidu.com/schema/myns"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.baidu.com/schema/myns 
http://www.baidu.com/schema/myns/myns.xsd">
    <myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true" />
</beans>
public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
    System.out.println(context.getBean("defaultDateFormat"));
}

上面的代码中会打印出SimpleDateFormat实例,Spring的自定义标签的解析很容易理解,主要是xsd文件的编写和自定义标签解析器的编写。很多与Spring集成的第三方框架都有自定义标签的解析的实现,比如Dubbo。