spring源码:解析xml中的import和alias标签

一、介绍

  spring允许xml中有两类标签:默认标签扩展标签 。

  1. 默认标签有四种:beans、bean、import、alias ;
    其中bean标签为最基础的标签,其他三个标签都是围绕bean标签来做封装或者修饰的
  • beans:包含多个bean标签
  • import:表示导入另外一个xml,xml又包含这些标签
  • alias:修饰某个bean的别名
  1. 扩展标签包括spring的扩展标签用户自定义的扩展标签,如:
<tx:annotation-driven/>

本篇主要分析默认标签中两个:import标签alias标签。

二、整体脉络

  1. spring读取xml,把xml文档转换为一个Document对象
  2. 循环处理这个document对象中的各个标签,根据标签的命名空间不同,来判断是默认标签还是扩展标签。
  3. 处理默认标签:根据子节点的标签类型,使用不同的方法处理
    3.1 解析bean标签:主要工作为把bean标签的信息封装到GenericBeanDefinition中,然后把该实例注册到一个Map中。
    3.2 解析beans标签:递归调用3.1
    3.3 解析import标签:递归调用2
    3.4 解析alias标签:复用3.1中的注册别名方法

三、相关类和方法

  • DefaultBeanDefinitionDocumentReader#parseBeanDefinitions:解析xml中标签的入口
  • DefaultBeanDefinitionDocumentReader#parseDefaultElement:解析默认标签
  • DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource:解析import标签
  • DefaultBeanDefinitionDocumentReader#processAliasRegistration:解析alias标签

四、源码分析

  1. 解析xml中标签入口,其中入参root就代表该xml文档的根节点
	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)) {
						// 1.解析默认标签
						parseDefaultElement(ele, delegate);
					}
					else {
						// 2.解析扩展标签
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}
  1. 解析默认标签
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			// 1.处理import标签(本篇讲述)
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			// 2.处理alias标签(本篇讲述)
			processAliasRegistration(ele);
		}
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			// 3.处理bean标签(参考历史文章)
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// 4.处理beans标签
			doRegisterBeanDefinitions(ele);
		}
	}

2.1 解析import标签

	protected void importBeanDefinitionResource(Element ele) {
		// 获取resource属性
		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		// 如果不存在resource属性 则不做处理
		if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}

		// 解析系统属性,格式如: e.g. "${user.dir}"
		location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

		Set<Resource> actualResources = new LinkedHashSet<>(4);

		// 判断location是绝对uri还是相对uri
		boolean absoluteLocation = false;
		try {
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
			// cannot convert to an URI, considering the location relative
			// unless it is the well-known Spring prefix "classpath*:"
		}

		// Absolute or relative?
		// 如果是绝对uri,则直接根据地址加载对应的配置文件
		if (absoluteLocation) {
			try {
				//根据指定路径加载xml文件  点进去
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
				}
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error(
						"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
			}
		}
		else {
			// No URL -> considering resource location as relative to the current file.
			// 如果是相对地址,则根据相对地址计算出绝地地址
			try {
				int importCount;
				// 使用ClassPathResource获取resource
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				if (relativeResource.exists()) {
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				else {
					String baseLocation = getReaderContext().getResource().getURL().toString();
					// location可能指定了多个地址,使用ResourcePatternResolver获取resource
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
				}
			}
			catch (IOException ex) {
				getReaderContext().error("Failed to resolve current resource location", ele, ex);
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
						ele, ex);
			}
		}
		// 解析后进行监听器激活处理
		Resource[] actResArray = actualResources.toArray(new Resource[0]);
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}

	public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				// 加载多个resources,相当于加载多个xml
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			Resource resource = resourceLoader.getResource(location);
			// 复用加载beanDefinition的方法
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}

	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int counter = 0;
		for (Resource resource : resources) {
			// 循环调用加载xml的方法
			counter += loadBeanDefinitions(resource);
		}
		return counter;
	}

	// 被复用加载BeanDefinition的方法,相当于重新开始加载一个xml
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

2.2 解析alias标签

	protected void processAliasRegistration(Element ele) {
		// 获取beanName属性值
		String name = ele.getAttribute(NAME_ATTRIBUTE);
		// 获取alias属性值
		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
		boolean valid = true;
		if (!StringUtils.hasText(name)) {
			getReaderContext().error("Name must not be empty", ele);
			valid = false;
		}
		if (!StringUtils.hasText(alias)) {
			getReaderContext().error("Alias must not be empty", ele);
			valid = false;
		}
		if (valid) {
			try {
				// 注册alias核心代码(同解析bean标签时分析的alias属性)
				getReaderContext().getRegistry().registerAlias(name, alias);
			}
			catch (Exception ex) {
				getReaderContext().error("Failed to register alias '" + alias +
						"' for bean with name '" + name + "'", ele, ex);
			}
			// 别名注册后,通知监听器做相应处理
			getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
		}
	}

注册alias的核心代码,跟解析bean标签的alias属性使用的是同一个方法。最终别名的信息会保存在SimpleAliasRegistry对象的aliasMap属性中。之前的文章讲过,里面逻辑也很简单,就不复述了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值