Spring源码情操陶冶-DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

前言-阅读源码有利于陶冶情操,本文承接前文Spring源码情操陶冶-AbstractApplicationContext#obtainFreshBeanFactory

前文提到最关键的地方是解析bean xml配置文件,废话不多说,直接上代码清单

//正如官方注释所说,解析import标签、alias标签、bean标签和自定义的标签
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//检查<beans>根标签的命名空间是否为空或者是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)) 
						//解析import标签、alias标签、bean标签、内置<beans>标签
						parseDefaultElement(ele, delegate);
					}
					else {
						//解析自定义标签,例如<context:component-scan basePackage="" />
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}

那我们根据两个部分来解读对bean xml文件的解析,分别为DefaultBeanDefinitionDocumentReader#parseDefaultElementBeanDefinitionParserDelegate#parseCustomElement

DefaultBeanDefinitionDocumentReader#parseDefaultElement

解析import标签、alias标签、bean标签,代码清单如下

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);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse 递归
			doRegisterBeanDefinitions(ele);
		}
	}

三种解析具体方法
1.DefaultBeanDefinitionDocumentReader#importBeanDefinitionResource
import标签解析代码比较长,逻辑倒是很简单,代码清单如下

	protected void importBeanDefinitionResource(Element ele) {
		//获取<import>标签的resource属性值
		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		//resource不允许为空
		if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}

		// Resolve system properties: e.g. "${user.dir}"
		//这里强调下,对于<import>标签,其会从System.getProperties()和System.getenv()中属性获取,优先System.getProperties()
		//即优先系统变量,环境变量次之
		location = environment.resolveRequiredPlaceholders(location);

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

		// Discover whether the location is an absolute or relative URI
		//resource属性支持URL模式,找到相应的资源并进行加载
		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?
		if (absoluteLocation) {
			try {
				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;
				//相对路径,相对于当前文件
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				if (relativeResource.exists()) {
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				else {
					String baseLocation = getReaderContext().getResource().getURL().toString();
					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[actualResources.size()]);
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}

2.DefaultBeanDefinitionDocumentReader#processAliasRegistration
主要目的就是缓存全局别名

	protected void processAliasRegistration(Element ele) {
		//这里确保<alias>标签的name、alias值不为空 e.g. <alias name="key" alias="value">
		String name = ele.getAttribute(NAME_ATTRIBUTE);
		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 {
				//全局设置,查阅后其会保存到DefaultListableFactory的父级类`SimpleAliasRegistry#aliasMap`中
				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));
		}
	}

3.DefaultBeanDefinitionDocumentReader#processBeanDefinition
<bean>标签的解析处理

	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		//初始化bean标签的相应内容
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			//对bean标签中的属性和子标签,进行相应的具体解析,例如property、constructor-args
			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));
		}
	}

BeanDefinitionParserDelegate#parseBeanDefinitionElement

解析bean标签,也许会返回null在出现解析错误的时候,代码清单如下

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
		//获取<bean>的 id属性
		String id = ele.getAttribute(ID_ATTRIBUTE);
		//如果有name属性,则获取name,并且可有多个name,以,;为分隔符
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

		List<String> aliases = new ArrayList<String>();
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}

		String beanName = id;
		//当id属性不存在且name属性有值时,默认使用第一个name值
		if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
			beanName = aliases.remove(0);
			if (logger.isDebugEnabled()) {
				logger.debug("No XML 'id' specified - using '" + beanName +
						"' as bean name and " + aliases + " as aliases");
			}
		}
		//确保beanName的唯一性,即在应用使用前不允许有两个的beanName一致
		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}
		//解析除id、name属性的情况
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			//省略部分代码
			...
			...
			String[] aliasesArray = StringUtils.toStringArray(aliases);
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}

限于篇幅太长,其余的情况我们就不讲解了,有兴趣的可以自行前往,但在此根据读到的源码对bean解析进行总结

  1. parent属性代表父类,这里表示的是父类的唯一id名

  2. 不允许有singleton属性;可有scope属性;
    可有abstract属性,值可为true/false;
    可有lazyinit属性,值为default则沿用beans标签中的lazyinit,否则为true/false;
    可有autowire属性,值为default则沿用beans标签的autowirebyName表示按照名称注入、byType按照类型注入、constructor表明按照构造函数注入、autodetect表明自动检测(官方不建议使用)、默认则不使用注入;
    可有dependency-check属性,值有allobjectssimple,默认为不检测;
    可有depends-on属性,可有多值,以,;为分隔符;
    可有autowire-candidate属性,值可为true/false/default/空字符串
    可有primary属性,值为true/false;
    可有init-method属性,初始化方法,对应可有destroy-method属性;
    可有facroty-method属性,工厂方法;
    可有factory-bean属性

  3. bean标签下可有description标签,表示对此bean的描述;
    也可有meta标签;
    可有lookup-method标签;
    可有replaced-method标签;
    可有constructor-arg标签;
    可有property标签,其中valueref属性只可选用一个;
    可有qualifier标签,其下可有attribute标签,同@Qualifier注解

  4. 所有的bean信息都由BeanDefinitionHolder对象来保存,其中的BeanDefinition包含了一个bean标签的所有可能内容

BeanDefinitionParserDelegate#parseCustomElement

解析自定义的标签节点,例如<context:component-scan><tx:annotation-driven>等,简单看下代码清单

    //containingBd此处为null
    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
		//获取命名空间
		String namespaceUri = getNamespaceURI(ele);
		//从map集合中获取NamespaceHandler接口
		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));
	}

按照上述的注释,我们需要查看下namespaceURI对应的NamespaceHandler到底是如何获取的,真实调用的是DefaultNamespaceHandlerResolver类,稍微分析下此类


构造函数

	public DefaultNamespaceHandlerResolver() {
		//第二个参数值为 META-INF/spring.handlers
		this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION);
	}
	
	//此处所用的是handlerMappingLocation,指的是handler接口类加载的文件资源
	public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) {
		Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null");
		this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
		this.handlerMappingsLocation = handlerMappingsLocation;
	}

resolve解析方法

		public NamespaceHandler resolve(String namespaceUri) {
		//调用的是PropertiesLoaderUtils.loadAllProperties读取所有classpath下的'META-INF/spring.handlers'文件,保存至Map集合
		Map<String, Object> handlerMappings = getHandlerMappings();
		Object handlerOrClassName = handlerMappings.get(namespaceUri);
		if (handlerOrClassName == null) {
			return null;
		}
		else if (handlerOrClassName instanceof NamespaceHandler) {
			return (NamespaceHandler) handlerOrClassName;
		}
		else {
			String className = (String) handlerOrClassName;
			try {
				Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
				//声明的handler必须是NamespaceHandler的实现类
				if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
					throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
							"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
				}
				//实例化Handler类
				NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
				//初始化
				namespaceHandler.init();
				//缓存下
				handlerMappings.put(namespaceUri, namespaceHandler);
				return namespaceHandler;
			}
			catch (ClassNotFoundException ex) {
				throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "] not found", ex);
			}
			catch (LinkageError err) {
				throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" +
						namespaceUri + "]: problem with handler class file or dependent class", err);
			}
		}
	}

限于篇幅太长,关于bean解析以及custom的具体解析未来会分条详细介绍,敬请期待

下节预告

Spring源码情操陶冶-AbstractApplicationContext#prepareBeanFactory

posted @ 2017-04-13 21:50  南柯问天  阅读(699)  评论(3编辑  收藏  举报