前面的几篇章节总体的介绍了springIOC容器的BeanDefinitiion加载及实例化和初始化的过程,本节再次总结一下BeanDefinition中元数据的加载
1:面向资源-springIOC容器面向资源加载bean的元信息,资源类型包括 xml文件,properties文件。具体的加载类是XmlBeanDefinitionReader和propertiesBeanDefinitionReader,这两个类是继承自
AbstractBeanDefinitionReader类,是兄弟类的关系,面向的资源不一样而已,统一实现了AbstractBeanDefinitionReader的抽象方法
方法,因为properties文件和xml文件在spring中都被抽象为Resource资源。
细看XmlBeanDefinitionReader的loadBeanDefinitions(Resource ...)方法
方法最后委托给BeanDefinitionDocumentReader 类加载 Document 。因为我们知道xml文件遵循W3C标准,读取到java中都是Document格式。
追踪代码到DefualtBeanDefinitionDocumentReader实现类中,
代码获取Document的Element元素,这个元素是根元素-root。W3C规定,Document中只能有一个跟元素。
然后调用了 parseBeanDefinitions(root,this.delegate)方法进行实际Bean元信息的加载
看具体实现
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
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);
}
}
代码中分为默认的命名空间元素处理和自定义的命名空间元素处理
即 “import” “alias” “bean”是默认的beans命名空间的元素,需要默认的处理-parseDefaultElement(ele,delegate);
其他的是客户自定义的命名空间元素,需要特殊的处理-delegate.parseCustomElement(ele);
首先看默认的parseDefaultElement(ele,delegate)方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//处理 bean元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
我们首要的看下 processBeanDefinition(ele.delegate)方法
/**
* Process the given bean element, parsing the bean definition
* and registering it with the registry.
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
方法的细节就是,获取创建一个新的BeanDefinitionHolder 然后包装BeanDefintion。beanDefinition中的数据是从Element中读取的
继续深追代码发现:
delegate.parseBeanDefinitionElement(ele);中先解析了Bean的属性,然后解析了Bean下的节点的属性
比如下面的xml,先解析了Bean的属性 “id”,"class","beanName"等,生成一个BeanDefinitionHodler,保存id,alias,beanName等属性,然后再解析<bean>下的节点-<property>节点,然后保存在BeanDefinition中。
<bean id = "user" class="com.david.study.spring.domain.User">
<property name="age" value="18"></property>
<property name="name" value="David"></property>
</bean>
这样,就能保证BeanDefinition元信息的正常解析,上面这是默认的元素解析,如果是客户自定义的元素,要怎么解析呢
回到上面的parseBeanDefinition(Element root,BeanDefinitionParseDelegate delegate)
我们看下自定义元素的解析
delegate.parseCustomElement(ele)方法
追踪方法到
/**
* Parse a custom element (outside of the default namespace).
* @param ele the element to parse
* @param containingBd the containing bean definition (if any)
* @return the resulting bean definition
*/
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 获取当前的元素的命名空间处理器
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));
}
简单的看,是获取了当前元素所在的命名空间的处理器。然后委托给处理器解析当前元素。
看下NamespaceHandler的定义:
/**
* Base interface used by the {@link DefaultBeanDefinitionDocumentReader}
* for handling custom namespaces in a Spring XML configuration file.
*
* <p>Implementations are expected to return implementations of the
* {@link BeanDefinitionParser} interface for custom top-level tags and
* implementations of the {@link BeanDefinitionDecorator} interface for
* custom nested tags.
*
* <p>The parser will call {@link #parse} when it encounters a custom tag
* directly under the {@code <beans>} tags and {@link #decorate} when
* it encounters a custom tag directly under a {@code <bean>} tag.
*
* <p>Developers writing their own custom element extensions typically will
* not implement this interface directly, but rather make use of the provided
* {@link NamespaceHandlerSupport} class.
*
* @author Rob Harrop
* @author Erik Wiersma
* @since 2.0
* @see DefaultBeanDefinitionDocumentReader
* @see NamespaceHandlerResolver
* 上面讲当前的NamespaceHandler接口,是在DefaultBeanDefintionDocumentReader中被用来处理客
* 户在xml中使用的自定义命名空间的。客户不用直接实现本接口,可以继承NamespaceHandlerSupport
* 类。在使用的时候要返回BeanDefinitionParser的实现类
*/
public interface NamespaceHandler {
}
简单的讲,这个类是个基础的接口,不推荐使用,可以继承NamespaceHandlerSupport类
/**
* Support class for implementing custom {@link NamespaceHandler NamespaceHandlers}.
* Parsing and decorating of individual {@link Node Nodes} is done via {@link BeanDefinitionParser}
* and {@link BeanDefinitionDecorator} strategy interfaces, respectively.
*
* <p>Provides the {@link #registerBeanDefinitionParser} and {@link #registerBeanDefinitionDecorator}
* methods for registering a {@link BeanDefinitionParser} or {@link BeanDefinitionDecorator}
* to handle a specific element.
*
* 这个接口帮助实现客户定制的命名空间处理器,实用策略模式让BeanDefinitionParse接口和BeanDefinitionDecorator接口实现类来完成Node解析成BeanDefinition的任务
*/
public abstract class NamespaceHandlerSupport implements NamespaceHandler {
}
NamespaceHandler的回调接口 BeanDefinition parse(Element ele,ParseContext parserContext)实现
/**
* Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
* registered for that {@link Element}.
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionParser parser = findParserForElement(element, parserContext);
return (parser != null ? parser.parse(element, parserContext) : null);
}
委托给了 BeanDefinitionParser 的实现类来解析Element。
看下BeanDefinitionParser的注释
/**
* Interface used by the {@link DefaultBeanDefinitionDocumentReader} to handle custom,
* top-level (directly under {@code <beans/>}) tags.
*
* <p>Implementations are free to turn the metadata in the custom tag into as many
* {@link BeanDefinition BeanDefinitions} as required.
*
* <p>The parser locates a {@link BeanDefinitionParser} from the associated
* {@link NamespaceHandler} for the namespace in which the custom tag resides.
*
* @author Rob Harrop
* @since 2.0
* @see NamespaceHandler
* @see AbstractBeanDefinitionParser
*/
public interface BeanDefinitionParser {
/**
* Parse the specified {@link Element} and register the resulting
* {@link BeanDefinition BeanDefinition(s)} with the
* {@link org.springframework.beans.factory.xml.ParserContext#getRegistry() BeanDefinitionRegistry}
* embedded in the supplied {@link ParserContext}.
* <p>Implementations must return the primary {@link BeanDefinition} that results
* from the parse if they will ever be used in a nested fashion (for example as
* an inner tag in a {@code <property/>} tag). Implementations may return
* {@code null} if they will <strong>not</strong> be used in a nested fashion.
* @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
* @param parserContext the object encapsulating the current state of the parsing process;
* provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
* @return the primary {@link BeanDefinition}
*/
@Nullable
BeanDefinition parse(Element element, ParserContext parserContext);
}
接口是用来在DefaultBeanDefinitionDocumentReader类中解析客户自定义命名空间元素的,这个元素是在<beans>标签下面第一级的元素。
所以综上所述,
springIOC容器委托XmlBeanDefinitionReader 读取xml资源
XmlBeanDefinitionReader 委托 DefaultBeanDefinitionDocumentReader来读取 document
DefaultBeanDefinitionDocumentReader 自己解析 beans命名空间下的tag
委托用户自定义的NamespaceHandler解析用户自定义的命名空间下的tag
NamespaceHandler 委托 自定义的BeanDefinitionParse解析 beans 标签下的第一级标签。解析出来的BeanDefinition注册到BeanDefinitionRegistry。
上面就是整个xml资源的读取解析过程,那么Dubbo最初使用的xml中的标签dubbo是怎么解析的,大家是不是能稍微理解了呢
<!-- dubbo应用服务名称 -->
<dubbo:application name="嘿嘿" logger="slf4j" />
下一节我们仔细讲下如何自定义namespace和如何定义自己的标签