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

前面我们降解了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实现:

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的一些实现,我们只需要根据自己的需要继承一下方法即可:

首先我们需要定义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。

猜你喜欢

转载自blog.csdn.net/wk19920726/article/details/108828451