Spring的BeanDefinitionReader详解

814 阅读5分钟

前言

  • 还是Spring的经典使用代码
    public class SpringClient {
      public static void main(String[] args) {
          Resource resource = new ClassPathResource("applicationContext.xml");
          DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
          BeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(factory);
          beanDefinitionReader.loadBeanDefinitions(resource);
    
          Student student = (Student) factory.getBean("student");
          System.out.println(student.getName() +"\n" + student.getAge());
      }
    }
    
  • 前面讲解过Resource和BeanFactory的创建,但是还需要有个组件来将它们关联起来。

本篇将会讲解到的内容

  • xml的解析涉及到的类。
  • xml解析的具体步骤。
  • Spring是如何将现实的内容进行抽象化,变成对应的类的,也就是面向对象。
    • 比如,BeanDefinitionReader、Environment、Document、BeanDefinition等等,这些接口都是哪些事物的抽象。
  • 如果没有手动配置bean的id,Spring会自动生成独一无二的id。
  • Resource和BeanFactory是如何关联的。
  • bean工厂起到哪些功能。
  • 最主要的就是Spring bean注册的具体过程。
  • xml解析完后的信息存储在哪个位置。
  • 涉及到哪些设计模式。

BeanDefinitionReader

  • 你可以将它称之为配置信息读取器
  • 是bean 定义读取器的简单接口。 使用资源和字符串位置参数指定加载方法。具体的 bean定义读取器当然可以为 bean 定义添加额外的加载和注册方法,特定于它们的 bean 定义格式。 image.png
  • 它的实现类image.png

XmlBeanDefinitionReader

它是BeanDefinitionReader的实现类

  • 构造方法image.png

    从构造方法看BeanDefinitionRegistryBeanFactory想必有些血缘关系。 下面介绍它的spuer()

AbstractBeanDefinitionReader

  • 为给定的 bean 工厂创建一个新的 AbstractBeanDefinitionReader。
  • 如果传入的 bean factory 不仅实现了 BeanDefinitionRegistry 接口,还实现了 ResourceLoader 接口,那么它也会被用作默认的 ResourceLoader。这通常是org.springframework.context.ApplicationContext的实现类。
  • 如果给定一个普通的 BeanDefinitionRegistry,默认的 ResourceLoader 将是一个PathMatchingResourcePatternResolver 。
  • 如果传入的 bean 工厂也实现了EnvironmentCapable它的环境将被这个读者使用。否则,reader将初始化并使用StandardEnvironment 。所有的 ApplicationContext 实现都是 EnvironmentCapable,而普通的 BeanFactory不是。image.png
  • 看看构造方法的代码image.png
  • 策略模式可以用来解决大量if...else的情况。
  • 策略模式(Strategy Pattern),定义了一系列的算法,将每一种算法封装起来并可以相互替换使用,策略模式让算法独立于使用它的客户应用可独立变化,接口就是定义行为的
  • 详细看看 Environment 接口image.png image.png

也就是说该接口表示配置文件和各种程序所需的属性。
至此,XmlBeanDefinitionReader,实例化完毕。

beanDefinitionReader.loadBeanDefinitions(resource)

loadBeanDefinitions(resource)方法简介

  • 将BeanFactory和Resource关联起来的关键;
  • 该方法会解析xml文件,并将信息解析成bean放置到Factory。显然该方法的工作量较大。
  • 方法说明如下image.png

看看源码

  • 会将Resource重新封装成EncodedResourceimage.pngimage.pngimage.pngimage.png

继续讲解loadBeanDefinitions(EncodedResource)的try...catch部分

doLoadBeanDefinitions(InputSource, Resource)方法

  • 上面讲到loadBeanDefinitions(EncodedResource)中的doLoadBeanDefinitions(InputSource, Resource)是其核心方法。
  • 在Spring中,do开头的方法一般都是protected的核心方法。
  • 先将XML变成Document对象。image.png
  • registerBeanDefinitions(Documen, Resource),解析和注册bean进容器image.png

    BeanDefinitionDocumentReader是用于解析包含 Spring bean 定义的 XML 文档的 SPI。由 XmlBeanDefinitionReader用于实际解析 DOM 文档。 image.png

  • 获取之前的Bean定义image.png
  • 创建XmlReaderContext,以便使用NamespaceHandler,它可以处理xml文件中自定义命名空间的问题image.pngimage.png
  • bean解析注册的具体过程。image.pngimage.png 重点看看 processBeanDefinition(ele, delegate);image.png --- 下面介绍改方法的局部变量 bdHolder 是怎么来的 --- image.png 有涉及到两个类 image.png image.png AbstractBeanDefinition是接口BeanDefinition的实现,里面有一个重要的成员变量image.png
    解析的过程中有个步骤image.png 还有创建独一无二的beanName的步骤 image.png
  • 如果xml文件中不指定id,Spring会给你生成一个beanName。image.png decorateBeanDefinitionIfRequired方法是根据基于自定义的东西进行装饰。
    --- 至此bdHolder创建完成了 ---
    --- 最后就是 registerBeanDefinition --- image.png 注意一下工厂中有个重要的成员变量 image.png 真正的注册阶段 QQ截图20220419000021.png image.png --- 至此bean就注册好了 ---
    --- processBeanDefinition(ele, delegate); 的最后 --- image.png
  • 历经重重,bean就算注册完了,要注意,这里是将xml中bean的信息解析之后,将信息存入BeanDefinition中。
  • 而BeanDefinition又在BeanDefinitionHolder中,只字未提bean的实例化,也就是bean的创建。这里只涉及bean的注册。

最后小结—Spring Bean注册的流程

  1. 定义好spring的配置文件。
  2. 通过Resource对象将Spring配置文件进行抽象,抽象成一个具体的Resource对象(如ClassPathResource) 。
  3. 定义好将要使用的Bean工厂(各种BeanFactory)。
  4. 定义好XmlBeanDefinitionReader对象,并将工广对象作为参数传递进去,从而构建好二者之间的关联关系。
  5. 通过XmlBeanDefinitionReader对象读取之前所抽取出的Resource对象。
  6. 流程开始进行xml解析。
  7. 针对xml文件进行各种元素以及元素属性的解析,这里面,真正的解析是通过BeanDefinitionParserDelegate对象来完成的(委托[Delegate]模式)。
  8. 通过BeanDefinitionParserDelegate对象在解析xml文件时,又使用到了模板方法模式(pre, process, post)。
  9. 当所有的bean标签元素都解析完毕后,开始定义一个BeanDefinition对象,该对象是一个非常重要的对象,里面容纳了一个Bean相关的所有信息。
  10. BeanDefinition对象创建完毕后,Spring又会创建一个BeanDefinitionHolder对象来持有这个BeanDefinition对象。
  11. BeanDefinitionHolder对象主要包含两部分内容: beanNameBeanDefinition还有bean的别名数组。
  12. 工厂会将解析出来的Bean信息存放到内部的一个ConcurrentHashMap中,该Map的键是beanName。(唯一),值是BeanDefinition对象。
  13. 调用Bean解析完毕的触发动作,从而触发相应的监听器的方法的执行(观察者模式)。
  • 再提一嘴,注意bean注册和创建(实例化)的区别。
  • 将Resource重新封装成EncodedResource,是装饰者模式
  • ResourceLoader是策略接口,是策略模式
  • 本篇就讲解了beanDefinitionReader.loadBeanDefinitions(resource);。这一行程序员手写的代码。