吃西瓜

走过必留下痕迹

目录
探索SpringBoot-Spring源码之对象是如何注册到IoC容器中的?(十一)
/  

探索SpringBoot-Spring源码之对象是如何注册到IoC容器中的?(十一)

前文回顾

之前探索SpringBoot系列也是到了探索SpringBoot-一起看看Spring源码之Resource(十)。之前有提到过Spring容器最重要的阶段分为三个,分别是Bean的发现,读取,注册。今天我们来看看Bean的注册。

看完本文你将会知道Spring如何将Xml中的标签转化为BeanDefinition,实在是山路十八弯啊

注册Bean

因为有了之前文章的铺垫,直接看XmlBeanDefinitionReader#registerBenDefinitions

    //对之前在准备工作中得到的Document和Resource进行解析和注册Bean
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException{
	    //使用默认的DefaultBeanDefinitionDocumentReader来实例化BeanDefinitionDocumentReader
		// Read document based on new BeanDefinitionDocumentReader SPI.
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		//在初始化XmlBeanDefinitionReader的时候,会默认将BeanFactory传递进入成为BeanDefinitionRegistry
		int countBefore = getRegistry().getBeanDefinitionCount();
		//加载及注册Bean
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		//记录本次加载的BeanDefinition的个数
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

经过了一系列的准备工作之后,我们终于进入到了实际加载和注册Bean的阶段。让我们继续进入到DefaultDocumentBeanDefinitionDocumentReader#registerBeanDefinitions(Document,XmlReaderContext)中。

    //进入到DefaultDocumentReader中对BeanDefinition进行解析
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	    //将readerContext上下文赋值给自己的属性
		this.readerContext = readerContext;
		//打印一个日志
		logger.debug("Loading bean definitions");
		//获得root
		Element root = doc.getDocumentElement();
		//获得专门的解析类
		BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
		//解析前处理,留给子类进行处理
		preProcessXml(root);
		parseBeanDefinitions(root, delegate);
		//解析后处理,留给子类进行处理
		postProcessXml(root);
	}

在这个函数中,主要做了两件事情。

  1. 获取专门的解析类
  2. 给子类留下处理的空间
  3. 解析root元素
    稍微提一下,这个preProcessXml(root)postProcessXml(root)方法都留给子类进行实现,如果子类需要在处理过程的前面和后面需要做一些处理。对设计模式熟悉一些,那么可以知道这里使用了模板模式
    继续看看parseBeanDefinitions(Element,BeanDefinitionParserDelegate)
    //解析root元素
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	    //如果root是默认的元素
		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 {
		//如果root是定制的元素
			delegate.parseCustomElement(root);
		}
	}

XML配置里面有两大类声明,一个是默认的如

<bean id="test" class="test.TestBean">

另一类就是自定义的,如

<tx:annotation-driven/>

Spring对于默认的标签和自定义的标签的解析有很大的不同,下面重点先解析一下默认的标签。进入到DefaultBeanDefinitionDocumentReader#parseDefaultElement中。

    //解析默认的元素
	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);
		}
	}

我们先看bean元素的解析,毕竟这是使用的最多的元素,也是IoC容器的核心过程。让我们进入到DefaultBeanDefinitionDocumentReader#processBeanDefinition(Element,BeanDefinitionParserDelegate)

	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	    //使用委托类解析元素为bdHolder
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
		//装饰bdHolder
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
			//委托给BeanDefinitionReaderUtils注册BeanDefinition到registry中
				// 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));
		}
	}

上述代码大致做了这么几件事情。

  1. 使用委托类解析元素为bdHolder,bdHolder元素中已经获得了解析完成后的id,name配置信息
  2. 如果bdHolder不为空,装饰bdHolder。那么什么时候为空呢?看完下一步的解析,可以看到当解析有问题的时候,会返回null。这里对解析失败的情况下,不会终止整个过程,还是继续解析下一个元素。
  3. 委托给BeanDefinitionReaderUtils注册BeanDefinition到registry中
  4. 通知事件
    每一个基本上都非常重要。我们一个一个来进行分析。进入到BeanDefinitionParserDelegate#parseBeanDefinitionElement(Element)
	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
		return parseBeanDefinitionElement(ele, null);
	}

继续进入都函数内部。代码可能会有点长。但是,已经到了最关键的时候了,我们要有点耐心。有耐心,才能理解别人不能理解的东西。

	public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
	//获取Id
		String id = ele.getAttribute(ID_ATTRIBUTE);
		//获取name
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
        //将name转化放入到aliases的别名数组中
		List<String> aliases = new ArrayList<String>();
		if (StringUtils.hasLength(nameAttr)) {
			String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
			aliases.addAll(Arrays.asList(nameArr));
		}
        //id就是beanName
		String beanName = id;
		//对id为空的情况,做一些默认的补救措施。逻辑是aliase去掉第一个元素,将aliase的第一个元素作为beanName
		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");
			}
		}

		if (containingBean == null) {
			checkNameUniqueness(beanName, aliases, ele);
		}
        //继续深入解析该元素的其他属性
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		//不为null,表示正常解析
		if (beanDefinition != null) {
			if (!StringUtils.hasText(beanName)) {
				try {
					if (containingBean != null) {
						beanName = BeanDefinitionReaderUtils.generateBeanName(
								beanDefinition, this.readerContext.getRegistry(), true);
					}
					else {
					//自己生成beanName
						beanName = this.readerContext.generateBeanName(beanDefinition);
						// Register an alias for the plain bean class name, if still possible,
						// if the generator returned the class name plus a suffix.
						// This is expected for Spring 1.2/2.0 backwards compatibility.
						String beanClassName = beanDefinition.getBeanClassName();
						if (beanClassName != null &&
								beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
								!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
							aliases.add(beanClassName);
						}
					}
					if (logger.isDebugEnabled()) {
						logger.debug("Neither XML 'id' nor 'name' specified - " +
								"using generated bean name [" + beanName + "]");
					}
				}
				catch (Exception ex) {
					error(ex.getMessage(), ele);
					return null;
				}
			}
			String[] aliasesArray = StringUtils.toStringArray(aliases);
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}

		return null;
	}

上面函数主要做了下面几个事情。

  1. 提取元素的idname属性
  2. 进一步解析其他所有的属性并统一封装到BeanDefinition
  3. 如果没有beanName,那么默认生成一个beanName
  4. 将获取的信息封装到BeanDefinitionHolder的实例中
    在上面的代码注释中,我稍微分析了下得到idname属性的过程,然后让我们继续深入看一下第2步的函数去。让我们看看到底是怎么得到一个beanDefinition的。
	public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {

		this.parseState.push(new BeanEntry(beanName));
		//如果有class的值,那么直接将className赋值
		String className = null;
		if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
			className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
		}

		try {
			String parent = null;
			//判断父类的节点
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			//实例化BeanDefinition对象
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);
			//解析基本的BeanDefinition属性
			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			//解析描述符
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
			//解析meta元素
			parseMetaElements(ele, bd);
			//解析look-up子元素
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			//解析replaceMethod子元素
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
			//解析构造器元素
			parseConstructorArgElements(ele, bd);
			//解析property元素
			parsePropertyElements(ele, bd);
			//解析Qulifier元素
			parseQualifierElements(ele, bd);
			//设置资源
			bd.setResource(this.readerContext.getResource());
			//设置source
			bd.setSource(extractSource(ele));

			return bd;
		}
		catch (ClassNotFoundException ex) {
			error("Bean class [" + className + "] not found", ele, ex);
		}
		catch (NoClassDefFoundError err) {
			error("Class that bean class [" + className + "] depends on not found", ele, err);
		}
		catch (Throwable ex) {
			error("Unexpected failure during bean definition parsing", ele, ex);
		}
		finally {
			this.parseState.pop();
		}

		return null;
	}

可以看到上述代码好了好多事情,能够清楚的看到自己在XML中配置的元素是如何被解析的。

  1. 获得className属性值
  2. 实例化一个BeanDefinition并将相关的属性赋值上去
  3. 解析各个子元素
    BeanDefinition是一个接口,定义了<bean>标签中所有属性值的操作。可以认为BeanDifinition<bean>标签在Spring中的抽象。Spring将配置文件中所有的bean标签都抽象为BeanDefinition,然后注册到BeanDefinitionRegistry中。

我们继续看看到底是怎么初始化一个BeanDefinition的实例的。

	protected AbstractBeanDefinition createBeanDefinition(String className, String parentName)
			throws ClassNotFoundException {
			//可以看到需要的参数是parentName,className,classLoader
		return BeanDefinitionReaderUtils.createBeanDefinition(
				parentName, className, this.readerContext.getBeanClassLoader());
	}

可以看到需要的参数是parentName,className,classLoader。关键因素是之后的classNameclassLoaderSpring在这里又使用BeanDefinitionReaderUtils来实例化BeanDefinition对象。我们继续看BeanDefinitionReaderUtils#createBeandefinition()

	public static AbstractBeanDefinition createBeanDefinition(
			String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
        //还是使用的new关键字来对对象实例化,而且使用的是GenericBeanDefinition
		GenericBeanDefinition bd = new GenericBeanDefinition();
		//设置父节点名称
		bd.setParentName(parentName);
		//只有在className不为空的时候,才使用classLoader来加载className
		if (className != null) {
		    //还得判断下classLoader是不是空,不为空,才加载
			if (classLoader != null) {
				bd.setBeanClass(ClassUtils.forName(className, classLoader));
			}
			else {
			    //如果有className就设置className
				bd.setBeanClassName(className);
			}
		}
		return bd;
	}

可以看到最关键的一点是还是使用的new关键字来初始话GenericBeanDefinition对象。可以在GenericBeanDefinition的注释上面看到,BeanDefinition总共有三个实现类。但是,最后自从Spring2.5之后,不再使用Rootchild了,而是使用Generic更加通用。

发现没有,越过千山万水,我们终于达到了真正解析bean标签的真正的属性的函数了。让我们揭开她的神秘的面纱。看一看BeanDelegete#parseBeanDefinitionAttributes

	public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
			BeanDefinition containingBean, AbstractBeanDefinition bd) {
			//scope属性
		if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
			// Spring 2.x "scope" attribute
			bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
			if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
				error("Specify either 'scope' or 'singleton', not both", ele);
			}
		}
		//singleton属性
		else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
			// Spring 1.x "singleton" attribute
			bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ?
					BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
		}
		else if (containingBean != null) {
			// Take default from containing bean in case of an inner bean definition.
			bd.setScope(containingBean.getScope());
		}
		//abstract属性
		if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
			bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
		}
		//lazy_init属性
		String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
		if (DEFAULT_VALUE.equals(lazyInit)) {
			lazyInit = this.defaults.getLazyInit();
		}
		bd.setLazyInit(TRUE_VALUE.equals(lazyInit));

		String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
		bd.setAutowireMode(getAutowireMode(autowire));

		String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE);
		bd.setDependencyCheck(getDependencyCheck(dependencyCheck));

		if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
			String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
			bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, BEAN_NAME_DELIMITERS));
		}
        .........

		return bd;
	}

可以看到经常被提到的scope属性,lazy-init属性,还是很多我们不太熟悉的属性。这些属性被不断地放入到BeanDefinition的实例中。

在将这些所有的属性都再次一一解析之后,放入到BeanDefinition中,至此,我们就完成了从XML<bean>标签到Spring内部的BeanDefinition的过程了。

在好好消化消化,告一个段落。

关于写作

以后这里每天都会写一篇文章,题材不限,内容不限,字数不限。尽量把自己每天的思考都放入其中。

如果这篇文章给你带来了一些帮助,可以动动手指点个赞,顺便关注一波就更好了。

如果上面都没有,那么写下读完之后最想说的话?有效的反馈和你的鼓励是对我最大的帮助。

另外打算把博客给重新捡起来了。欢迎大家来访问吃西瓜

我是shane。今天是2019年8月24日。百天写作计划的第三十一天,31/100。


标题:探索SpringBoot-Spring源码之对象是如何注册到IoC容器中的?(十一)
作者:yang2yang
地址:http://blog.chixigua.xyz/articles/2019/08/24/1566659068467.html

评论