SpringIOC源码解析(5)—— 通过document对象解析出BeanDefinition实例

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: locationBeanDefinitionReaderResourceBeanDefinitionRegistryResourceloaderDefaultListableBeanFactory

《SpringIOC源码解析(3)—— Resource、ResourceLoader、容器之间的微妙关系》文末,将xml文件解析成了Document对象,接下来就该调用registerBeanDefinitions来解析Document对象。


public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   // 创建BeanDefinitionDocumentReader,这个是实际从XML的DOM树中读取BeanDefiniton
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   // 获取注册表beanDefinitionMap的在本次加载前的BeanDefinition数量
   int countBefore = getRegistry().getBeanDefinitionCount();
   // 加载并注册
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   // 本次加载注册后容器里BeanDefinition的数量减去先前的,即本次加载的BeanDefinition数量
   return getRegistry().getBeanDefinitionCount() - countBefore;
}


先看一下

createBeanDefinitionDocumentReader方法:


protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
   return BeanUtils.instantiateClass(this.documentReaderClass);
}

该方法会传入一个documentReaderClass实例对象,传入的的class如下:


private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
      DefaultBeanDefinitionDocumentReader.class;


DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的默认实现


0.png


registerBeanDefinitions的倒数第三行和倒数第一行代码就揭示了之前说的loadBeanDefinitions如何获取到本次加载的BeanDefinition的数量:


加载前先统计下注册表中BeanDefinition的数量

加载后再去统计下注册表中BeanDefinition的数量,两者计算差值

统计BeanDefinition的数量的方法:


@Override
public int getBeanDefinitionCount() {
   return this.beanDefinitionMap.size();
}

this就是上面getRegistry()返回的DefaultListableBeanFactory实例,DefaultListableBeanFactory实现了BeanDefinitionRegistry接口。


beanDefinitionMap是BeanDefinition实例最终注册到的地方


接下来

继续分析registerBeanDefinitions:


// 加载并注册
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

进入到registerBeanDefinitions方法


@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   doRegisterBeanDefinitions(doc.getDocumentElement());
}

readerContext是经过包装的Resource对象,调用了doRegisterBeanDefinitions这个真正干活的do方法。


protected void doRegisterBeanDefinitions(Element root) {
   // Any nested <beans> elements will cause recursion in this method. In
   // order to propagate and preserve <beans> default-* attributes correctly,
   // keep track of the current (parent) delegate, which may be null. Create
   // the new (child) delegate with a reference to the parent for fallback purposes,
   // then ultimately reset this.delegate back to its original (parent) reference.
   // this behavior emulates a stack of delegates without actually necessitating one.
   // BeanDefintion解析委托类
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createDelegate(getReaderContext(), root, parent);
   // 判断这个根节点是否是默认的命名空间,
   // 底层就是判断这个根节点的nameSpaceUrl=="http://www.springframework.org/schema/beans"
   if (this.delegate.isDefaultNamespace(root)) {
      // 获取这个profile属性的值,表示剖面,用于设置环境
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         // 根据分隔符换换成数组
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         // We cannot use Profiles.of(...) since profile expressions are not supported
         // in XML config. See SPR-12458 for details.
         // 判断这个切面是否是激活的环境,如果不是直接返回,表示这个配置文件不是当前运行环境的配置文件
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            if (logger.isDebugEnabled()) {
               logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return;
         }
      }
   }
   // 在解析xml之前做的准备工作,其实什么也没做
   preProcessXml(root);
   // 调用这个方法,解析
   parseBeanDefinitions(root, this.delegate);
   // 后续处理的
   postProcessXml(root);
   this.delegate = parent;
}


BeanDefinitionParserDelegate是个代理类,一会儿会委托这个对象来加载Document对象,this.delegate是具体的实例。


this.delegate.isDefaultNamespace(root)判断该xml是否是按照Spring定义的规范来编写的,如果是Spring定义的规则的话,就会尝试去获取profile标签:

String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);


profile主要用来区分项目的环境,可以用来区分线上或线下的环境(foodie的dev和product)

通过了上面的验证之后,doRegisterBeanDefinitions最后有两个钩子方法,方便去做定制化的事前和事后的处理,默认是没有任何实现。


preProcessXml(root);
postProcessXml(root);


两个钩子方法中间的是:


parseBeanDefinitions(root, this.delegate);


接收传进来的Document对象和传进来的BeanDefinitionParserDelegate对象


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;
            // Bean定义的Document对象使用了Spring默认的XML命名空间
            if (delegate.isDefaultNamespace(ele)) {
               // 若是则按照spring原有的逻辑进行解析
               parseDefaultElement(ele, delegate);
            }
            else {
               // 否则使用扩展的自定义代理类进行解析
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      // 使用扩展的自定义代理类进行解析
      delegate.parseCustomElement(root);
   }
}


 上面的代码会先判断Document是否符合Spring的规定,否则就有可能是用户自定义的另外一套的识别方案了。一般情况下是会进入到if里的,先从根部开始获取所有的子节点,并配合代理类对象delegate去解析,最终会调用parseDefaultElement。


 在方法里会根据不同的标签去处理不同的逻辑

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   // 如果元素节点是<Import>导入元素,进行导入解析
   // <import resource="classpath:applicationContext-datasource.xml"/>
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   // 如果元素节点是<Alias>别名元素,进行别名解析
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   // 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
   // 按照Spring的Bean规则解析元素
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   // 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
   // 按照Spring的Bean规则解析元素
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}


注册完毕后会发送一个事件进行通知。这里我们主要关注与bean标签相关的处理逻辑。processBeanDefinition(ele, delegate);


protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
   // 对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance.
         // 向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      // 在完成BeanDefinition注册之后,往容器发送注册完成的事件
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

 这里终于开始调用delegate.parseBeanDefinitionElement(ele)来解析dom元素了,之所以层层调用,主要是考虑到了程序扩展性,中间有各种钩子和子类方法。


 使用BeanDefinitionHolder来承接解析出来的BeanDefinition对象,本质上就是一个包装类


1.png

回到上面第4行调用的方法:


@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null);
}


这里第二个参数传null表示bean标签没有嵌套的子bean标签。


public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
   // 获取<Bean>元素中的id属性值
   String id = ele.getAttribute(ID_ATTRIBUTE);
   // 获取<Bean>元素中的name属性值
   // <bean class="study.spring.bean.Bean" id="bean1" name="firstBean 1stBean"></bean>
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
   List<String> aliases = new ArrayList<>();
   // 将<Bean>元素中的所有name属性值存放到别名中
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }
   String beanName = id;
   if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      beanName = aliases.remove(0);
      if (logger.isTraceEnabled()) {
         logger.trace("No XML 'id' specified - using '" + beanName +
               "' as bean name and " + aliases + " as aliases");
      }
   }
   // 元素中是否包含嵌套<Bean>元素
   if (containingBean == null) {
      // 检查<Bean>元素所配置的id、name或者别名是否重复
      checkNameUniqueness(beanName, aliases, ele);
   }
   // 对 <bean> 标签的其他属性进行解析
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   // 生要给BeanDefinition起名字
   if (beanDefinition != null) {
      if (!StringUtils.hasText(beanName)) {
         try {
            if (containingBean != null) {
               beanName = BeanDefinitionReaderUtils.generateBeanName(
                     beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
               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.isTraceEnabled()) {
               logger.trace("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;
}


该方法有很大一段逻辑是关于起名的,这里主要关注起名之后的parseBeanDefinitionElement方法

该方法返回一个AbstractBeanDefinition类型的实例,这就是我们一直要寻找的BeanDefinition实例了

@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, @Nullable BeanDefinition containingBean) {
   this.parseState.push(new BeanEntry(beanName));
   String className = null;
   // 如果<Bean>元素中配置了class属性,则获取class属性的值
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }
   String parent = null;
   // 如果<Bean>元素中配置了parent属性,则获取parent属性的值
   if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
      parent = ele.getAttribute(PARENT_ATTRIBUTE);
   }
   try {
      // 根据<Bean>元素配置的class名称和parent属性值创建BeanDefinition
      // 为载入Bean定义信息做准备
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);
      /**解析<bean>标签里的各种属性值,将其set进bd里面*/
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
      parseMetaElements(ele, bd);
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
      parseConstructorArgElements(ele, bd);
      parsePropertyElements(ele, bd);
      parseQualifierElements(ele, bd);
      bd.setResource(this.readerContext.getResource());
      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;
}


方法里主要是解析bean标签的其他属性值。

进入到createBeanDefinition中,发现这里面创建出了BeanDefinition实例


protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
      throws ClassNotFoundException {
   return BeanDefinitionReaderUtils.createBeanDefinition(
         parentName, className, this.readerContext.getBeanClassLoader());
}

进入到Util的createBeanDefinition中发现最终返回的是GenericBeanDefinition实例,从这里可以看到GenericBeanDefinition已经成为了Spring中主要的BeanDefinition


public static AbstractBeanDefinition createBeanDefinition(
      @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
   GenericBeanDefinition bd = new GenericBeanDefinition();
   bd.setParentName(parentName);
   if (className != null) {
      if (classLoader != null) {
         bd.setBeanClass(ClassUtils.forName(className, classLoader));
      }
      else {
         bd.setBeanClassName(className);
      }
   }
   return bd;
}

 回到parseBeanDefinitionElement方法,剩下的内容主要就是对bean剩下的内容解析,解析完成之后会将相应的属性值set到BeanDefinition里。最后返回GenericBeanDefinition实例。


执行完毕后又回到DefaultBeanDefinitionDocumentReader的processBeanDefinition方法里

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // BeanDefinitionHolder是对BeanDefinition的封装,即Bean定义的封装类
   // 对Document对象中<Bean>元素的解析由BeanDefinitionParserDelegate实现
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance.
         // 向Spring IOC容器注册解析得到的BeanDefinition,这是BeanDefinition向IOC容器注册的入口
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      // 在完成BeanDefinition注册之后,往容器发送注册完成的事件
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}


此时已经执行完了


BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

之后还会看一下是否需要根据用户自定义的逻辑来处理额外的一些属性

bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

这一步属于定制化扩展,关键的是


BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());


调用该方法会向容器里注册BeanDefinition,也就是往map中写入,注册完成后往容器发送注册完成的事件。


执行完毕之后又回到parseDefaultElement方法,当遇到<beans>标签后证明会有很多<bean>标签,然后执行doRegisterBeanDefinitions(ele);方法完成和上面相同的步骤。


private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   // 如果元素节点是<Import>导入元素,进行导入解析
   // <import resource="classpath:applicationContext-datasource.xml"/>
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   // 如果元素节点是<Alias>别名元素,进行别名解析
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   // 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
   // 按照Spring的Bean规则解析元素
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   // 元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
   // 按照Spring的Bean规则解析元素
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}
相关文章
|
29天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
67 2
|
13天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
17天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
47 12
|
15天前
|
存储 网络协议 算法
【C语言】进制转换无难事:二进制、十进制、八进制与十六进制的全解析与实例
进制转换是计算机编程中常见的操作。在C语言中,了解如何在不同进制之间转换数据对于处理和显示数据非常重要。本文将详细介绍如何在二进制、十进制、八进制和十六进制之间进行转换。
27 5
|
23天前
|
存储 机器学习/深度学习 编解码
阿里云服务器计算型c8i实例解析:实例规格性能及使用场景和最新价格参考
计算型c8i实例作为阿里云服务器家族中的重要成员,以其卓越的计算性能、稳定的算力输出、强劲的I/O引擎以及芯片级的安全加固,广泛适用于机器学习推理、数据分析、批量计算、视频编码、游戏服务器前端、高性能科学和工程应用以及Web前端服务器等多种场景。本文将全面介绍阿里云服务器计算型c8i实例,从规格族特性、适用场景、详细规格指标、性能优势、实际应用案例,到最新的活动价格,以供大家参考。
|
1月前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
21天前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
2月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
75 0
|
2月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
57 0
|
2月前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
65 0

推荐镜像

更多