spring源码05: DOM树默认名称空间解析

// XmlBeanDefinitionReader.java
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {

		try {
			// 1.将文件流转化为DOM树
			Document doc = doLoadDocument(inputSource, resource);
			// 2.DOM树转化为BeanDefinition,并注册到对应map中
			int count = registerBeanDefinitions(doc, resource);
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + count + " bean definitions from " + resource);
			}
			return count;
		}
		catch () {
			throw ...;
		}
	
	}

上节我们讲了inputStream -> DOM树的过程,也就是备注的第一点,现在我们从第二点,开始继续往下阅读。

// XmlBeanDefinitionReader.java
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		// 真正开始解析DOM树,生产并注册BeanDefinitions
		doRegisterBeanDefinitions(doc.getDocumentElement());
	}

DOM树 -> BeanDefinition

  1. 创建解析DOM树的委托(delegate
  • spring委托delegate进行DOM树的解析,其中需要注意的是<beans>存在嵌套,假如子<beans>中无default-*的相关信息,默认他需要继承外层<beans>的配置信息
  • preProcessXml,postProcessXml是2个没有final修饰的空方法(该设计思想非常重要,画重点!),典型的设计模式中的模板方法模式。该方法留给子类继承并完成override,让用户有机会能够干预spring的各个环节。spring大量的使用这种设计模式,包括我们后面要重点讲的后置处理器(spring BeanPostProcessor 生命周期
// DefaultBeanDefinitionDocumentReader.java
protected void doRegisterBeanDefinitions(Element root) {
		/**
		 * delegate 指的是委托的意思,spring委托他进行DOM树的解析
		 *
		 * Xml文件中的<beans>标签是允许嵌套的,当出现嵌套时势必引起函数的递归调用,
		 * 为了防止外层<beans>标签的default-*相关信息在子委托中丢失,确保内存<beans>能正确继承default-*相关信息
		 * spring使用了堆栈的思想,先保存父委托,在将父委托传入以确保子委托的正确继承创建,函数最后即子委托结束后,再还原父委托
		 */
		BeanDefinitionParserDelegate parent = this.delegate;
		this.delegate = createDelegate(getReaderContext(), root, parent);

		if (this.delegate.isDefaultNamespace(root)) {
			/**
			 * profile属性,用于在一个xml中实现多套配置
			 * 例如切换开发和生产环境:
			 * <beans profile = "dev"> ... </beans>
			 * <beans profile = "pro"> ... </beans>
			 */
			String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
			if (StringUtils.hasText(profileSpec)) {
				String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
						profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
				// We cannot use Profiles.of(...) since profile expressions are not supported
				// in XML config. See SPR-12458 for details.
				// 尝试在环境中指定profile可以接收的bean
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					if (logger.isDebugEnabled()) {
						logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
								"] not matching: " + getReaderContext().getResource());
					}
					return;
				}
			}
		}

		// 预留给子类实现,在解析前进行自定义操作
		preProcessXml(root);
		// 解析及注册BeanDefinition,核心逻辑
		parseBeanDefinitions(root, this.delegate);
		// 预留给子类,解析后的操作
		postProcessXml(root);

		this.delegate = parent;
	}
  1. 解析DOM树

为了更好抓住主线,我们只分析默认名称空间的解析

// DefaultBeanDefinitionDocumentReader.java
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		// 默认名称空间,本质是比较namespace是否等于"http://www.springframework.org/schema/beans"
		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);
		}
	}
  1. 解析默认名称空间

代码阅读到这里我们看到了几个熟悉的常量,这里不打算每个标签都进行讲解,还是抓住主线

  • import:<import resource = “config.xml”> 该标签递归调用加载资源,将重点分析
  • bean:spring最重要的标签,重点分析
  • alias:别名标签,该标签留给读者自行分析
  • beans:该标签递归调用doRegisterBeanDefinitions,这里将会用到之前讲到的标签嵌套,以及parent delegate继承的使用。留给读者自行分析
// DefaultBeanDefinitionDocumentReader.java
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		// 解析import标签
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		// 解析alias标签
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		// 解析bean标签
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		// 解析beans标签,解析该标签实际是递归调用doRegisterBeanDefinitions,前面提到的parent
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

至此spring解析标签的准备工作才算做完,接下去标签的解析、注册才算刚刚开始

发布了30 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/chaitoudaren/article/details/104833489