探索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。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章