Spring源码深度解析(二)

时间:2022-07-24
本文章向大家介绍Spring源码深度解析(二),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Spring源码深度解析(二)

一. 主要内容

  1. BeanFactory体系设计
  2. 核心接口
  3. BeanDefinition加载流程
  4. 设计思想总结

二. BeanFactory体系设计

BeanFactory是Spring中十分重要的接口,也是Spring IOC容器的顶级接口。它基于工厂模式,定义了最基本的IOC容器的功能,如获取Bean实例、查看Bean的类型和查看Bean是否存在等:

在BeanFactory的基础上扩展了很多接口,每个接口都有各自的功能。BeanFactory的继承体系如下:

三. 各种BeanFactory

  1. ListableBeanFactory:可以根据条件列出Bean的清单
  1. AutowireCapableBeanFactory:提供了Bean自动注入的支持。
  2. HierarchicalBeanFactory:提供了容器继承的支持,可以维护一个parentBeanFactory父容器的引用,并通过它获取到父容器中的Bean。
  1. ConfigurableBeanFactory:可对BeanFactory进行各种配置
  2. DefaultListableBeanFactory:BeanFactory的默认实现,实现了上述所有接口的功能,是BeanFactory的集大成者。
  3. XmlBeanFactory:DefaultListableBeanFactory的子类,继承了DefaultListableBeanFactory的所有功能,并在此基础上扩展了XML文件相关的定制化处理。

可以看出,Spring很好地贯彻了面向接口编程的原则,几乎所有的重要的类上面都定义了接口。而且,通过接口的继承,可以灵活地扩展出不同功能的接口。

四. 核心接口

  1. BeanDefinition:对一个Bean实例的描述,包括其类型、属性、构造器、依赖等。 之前看过一个比喻:BeanFactory相当于一个汽车制造工厂,Bean对应于工厂中造出的各种车,而BeanDefinition就是每种车的图纸,描述了车的品牌、型号、所需零件、性能参数等。
  2. BeanDefinitionRegistry:BeanDefinition注册中心,定义了对 BeanDefinition 的各种增删改操作。
  1. AliasRegistry:别名注册中心。Spring支持为一个Bean定义多个别名。
  2. SingletonBeanRegistry:单例Bean注册中心。Spring会缓存所有Singleton类型的Bean,每次获取到的都是同一个。 BeanDefinition在定义了Bean的Scope,常用的有Singleton和Prototype,还可以扩展Scope接口进行自定义。 String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
  3. Resource:Spring对底层资源的封装,抽象了所有Spring内部可以使用到的底层资源,并提供了一些相关的查询和操作方法

对于不同来源的Resource,Spring都提供了对应的实现类,常用的有文件系统资源FileSystemResource和类路径资源ClassPathResource等。

五. BeanDefinition加载流程解析

BeanDefinition加载是Spring最重要的一个步骤,只有加载了所有定义的BeanDefinition后,才能根据其创建指定Bean,注入依赖,并完成后续一系列功能。BeanDefinition加载是在容器启动时自动完成的。

书中以XmlBeanFactory为例,讲解了BeanDefinition的加载和注册流程。虽然现在都以注解方式定义Bean,XML文件方式很少使用,但两种方式只是BeanDefinition的来源不同,核心处理流程是类似的,因此仍然以书中的内容为准。至于注解方式加载BeanDefinition的流程,如果感兴趣可以看下AnnotationConfigApplicationContext类。

XmlBeanFactory加载BeanDefinition的流程大致可以分为三个核心步骤:

  1. 资源的定位
  2. 将资源文件的解析为BeanDefinition集合
  3. 将解析好的BeanDefinition注入到容器中

六. 资源定位

可以显式指定BeanDefinition所在资源的位置,如书中代码所示:

Resource resource = new ClassPathResource("applicationContext.xml");
XmlBeanFactory beanFactory = new XmlBeanFactory(resource);

这样显式Resource的方式省去了资源定位的步骤,但是,实际上没有这么用的,现在使用的都是ApplicationContext接口,其加载资源的处理在AbstractRefreshableApplicationContext#refreshBeanFactory()中,后面章节有具体叙述。

七. BeanDefinition解析

不管基于何种方式,现在拿到了BeanDefinition所在的资源文件Resource,则创建XmlBeanFactory,执行BeanDefinition解析的工作。

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
  super(parentBeanFactory);

  //加载配置文件
  this.reader.loadBeanDefinitions(resource);
}

XmlBeanFactory在维护了一个XmlBeanDefinitionReader实例,其功能就是从XML配置文件中解析出BeanDefinition。XmlBeanDefinitionReader是BeanDefinitionReader接口的实现类,BeanDefinitionReader接口的作用就是解析BeanDefinition。

解析过程中的核心类:

  1. XmlBeanFactory:XML形式的Bean工厂。
  2. XmlBeanDefinitionReader:负责从XML配置文件中读取BeanDefinition。
  3. DocumentLoader:专注于XML解析,将执行的XML文件解析成一个Document对象。
  4. BeanDefinitionDocumentReader:针对DocumentLoader解析好的Document,从中加载出BeanDefinition,并注册到容器中。
  5. BeanDefinitionParserDelegate:BeanDefinitionDocumentReader内部维护的委托对象,创建BeanDefinition实例并对其进行包装。
  6. BeanDefinitionReaderUtils:最终执行实际的BeanDefinition的注册。

XmlBeanFactory并没有自己完成BeanDefinition解析的工作,而是委托给各种不同的类去处理,每个类只关注一项具体的功能,这也是单一职责原则的很好的体现。

加载BeanDefinition流程:

八. BeanDefinition注册

九. 设计思想总结

  1. 工厂模式
  2. 面向接口编程
  3. 单一职责原则
  4. 策略模式:BeanDefinitionReader的作用是读取BeanDefinition。根据BeanDefinition的来源不同,Spring定义了不同的实现类(XmlBeanDefinitionReader、GroovyBeanDefinitionReader、PropertiesBeanDefinitionReader),并在相应的BeanFactory处指定了具体的实现类(XmlBeanFactory指定使用XmlBeanDefinitionReader)。
  5. 模板方法模式:参见DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions()方法:
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.

  //BeanDefinitionParserDelegate代理对象,主要用于BeanDefinition的解析与注册
  BeanDefinitionParserDelegate parent = this.delegate;
  this.delegate = createDelegate(getReaderContext(), root, parent);

  //profile属性处理,只解析满足当前profile的BeanDefinition
  if (this.delegate.isDefaultNamespace(root)) {
    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;
      }
    }
  }

  //解析前置处理,模板方法,留给子类实现
  preProcessXml(root);

  //实际的BeanDefinition解析与注册
  parseBeanDefinitions(root, this.delegate);

  //解析前置处理,模板方法,留给子类实现
  postProcessXml(root);

  this.delegate = parent;
}
  1. 在父类方法中定义了方法的整体流程,并给出了核心实现parseBeanDefinitions()。在核心逻辑的前后,各定义了一个模板方法,交由子类进行定制化的处理。