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。
上一篇: Cuda 学习笔记 (一)