《spring设计思想》19-xml资源读取BeanDefinition-dubbo配置的bean的基础原理

前面的几篇章节总体的介绍了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和如何定义自己的标签

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值