之前 探索SpringBoot
系列也是到了 探索SpringBoot-一起看看Spring源码之Resource(十)
。之前有提到过 Spring
容器最重要的阶段分为三个,分别是 Bean的发现,读取,注册
。今天我们来看看 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); } 复制代码
在这个函数中,主要做了两件事情。
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)); } } 复制代码
上述代码大致做了这么几件事情。
id
, name
配置信息 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; } 复制代码
上面函数主要做了下面几个事情。
id
和 name
属性 BeanDefinition
中 beanName
,那么默认生成一个 beanName
BeanDefinitionHolder
的实例中
在上面的代码注释中,我稍微分析了下得到 id
和 name
属性的过程,然后让我们继续深入看一下第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
中配置的元素是如何被解析的。
className
属性值 BeanDefinition
并将相关的属性赋值上去 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
。关键因素是之后的 className
和 classLoader
。 Spring
在这里又使用 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
之后,不再使用 Root
和 child
了,而是使用 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。