【Spring】 BeanFactory 和 ApplicationContext 体系结构

0 阅读6分钟

当我们在进行 Java 开发的时候、是否遇见过如下的场景:

  • 当我们无法注入某个对象时,我们不得不写一个工具类并注入ApplicationContext对象、通过其 getBean() 方法获取我们需要的对象,如在使用 easyexcel 时、需要在其Listener进行数据库查询操作时、注入具体的 Dao 操作对象。
  • 项目中某两个模块在开发前期没有设计好出现 service 层互相依赖导致项目无法正常启动,如用户模块和部门模块,有时需要部门ID查询所有的职员、有时又需要从用户模块反查部门信息;此时为什么使用@Lazy注解就可以正常启动。
  • 常见的 Spring 面试题都能看见的事务失效、循环依赖、Bean的生命周期或者创建过程等。
  • 当我们启动项目需要事先加载一些资源时为什么有的会使用 @PostConstruct、有的采用实现 ApplicationRunner

为了彻底弄明白上述问题以及更好的处理日常开发中遇到的问题,我便较为深入和系统的学习了 Spring 相关的知识。基于此、便有了打算将自己对 Spring 的理解转换成自己的语言和文字的想法。主要分为如下板块:

  • BeanFactoryApplication 的体系结构
  • BeanFactoryBean 的扩展接口
  • Bean的创建过程
  • Aop的实现和Spring动态代理对象的创建
  • Spring 的事件发布机制
  • 一些知识点的补充

本文是第一篇,主要内容为了解 BeanFactoryApplicationContext 体系结构。了解这一部分内容之后,能够较好的理解单一职责原则。

当我们准备换工作或者刷面试资料时,有时候会看见这么一句话 BeanFactory 是清水房、ApplicationContext 是精装修。他们之间到底是什么关系那么就首先从 BeanFactory 谈起。

BeanFactory 体系结构

BeanFactory:是 Spring 容器的根接口、主要定义了获取单个 bean 的方法以及 FactoryBean的前缀FACTORY_BEAN_PREFIX = "&"BeanFactory 有三个直接子接口:

  • ListableBeanFactory
  • HierarchicalBeanFactory
  • AutowireCapableBeanFactory

ListableBeanFactory:提供了 "枚举" 获取 bean 的方法,这里的枚举不应当理解为 java中的枚举类,而是表示批量获取 bean的能力,在 BeanFactory 定义的方法中均是对获取单个 bean的重载,它是不具备获取某一类的所有 bean的能力:如获取某个接口的所有实现的实例。

批量获取Bean的能力在Spring的很多地方都有用到、如获取Spring中的事件监听器。

HierarchicalBeanFactory :表示具有层级能力 BeanFactory,即子容器可以获取父容器中的 bean。如在传统的SSM项目中,配置了不同的Spring容器, controller 层可以访问 service/dao 层中的 bean 的使用。主要有两个方法一个是getParentBeanFactory 另一个是containsLocalBean

传统SSM项目中的容器配置示例:

<!-- 父容器 -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 子容器 -->
<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/spring-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

AutowireCapableBeanFactory:主要用于系统集成或者非 Spring 管理的对象需要依赖注入时、创建或者装配 Bean、其主要定义了和 bean 相关的方法:creteBean() 、autowireBean()、configureBean()、initializeBean()以及一些钩子函数的执行,可以看出这是当一个 bean 不被 spring 创建时,自己创建完成之后又纳入 spring 的管理。

可以看出这三个基本是都是获取 bean 相关的,那么有没有和增强 BeanFactory 能力相关的呢?

ConfigurableBeanFactory:这个接口继承自 HierarchicalBeanFactory 接口;提供了配置BeanFactory 的能力,如setParentBeanFactoryaddBeanPostProcessor。除此之外还继承了DefaultSingletonBeanRegistry;这个类提供了三级缓存和 bean 的注册;

ConfigurableListableBeanFactory:继承自 ConfigurableBeanFactoryListableBeanFactoryAutowireCapableBeanFactory、这三个接口、不仅是在 ConfigurableBeanFactory 的基础上增加了枚举能力(批量获取对象的能力)

这两个接口主要是用于框架内部使用,普通的业务开发不建议使用。

上述的都是接口,在这里可能会有疑问为什么需要定义这么多接口呢?放在一个接口不行吗?实际上每一个接口都不是多余的。每一个接口都是对一个能力的抽象,如最基础的 BeanFactory、批量获取的 ListableBeanFactory、可以配置 BeanFactoryConfigurableBeanFactoryConfigurableListableBeanFactory。这体现了单一职责原则。

AbstractBeanFactory:看名字就应当清楚这是一个抽象类,是对 BeanFactory 的基础实现,提供了单例缓存、单例/原型判定、FactoryBean 处理、别名处理、子 Bean 定义的 Bean 定义合并,以及 Bean 销毁。

002_AbstractBeanFactory.jpg

子类需要实现的主要模板方法是getBeanDefinitioncreateBean,分别用于根据给定的 Bean 名称检索 Bean 定义,以及根据给定的 Bean 定义创建 Bean 实例。除此之外这个类定义了如何去获取一个 bean,即 doGetBean 方法。主要步骤可以简述为如下:

  • 从一级二级三级缓存中找
  • 从缓存中找不到就从父级容器中找
  • 再找不到就创建即调用 createBean 方法,而此方法延迟到了其子类实现。

AbstractAutowireCapableBeanFactory:继承自AbstractBeanFactory,实现了AutowireCapableBeanFactory这个接口。主要就是实现了 createBean 方法和配置相关的方法,具体创建的逻辑会在后面的文章分析。但是看名称这个类还是一个抽象类,因此就不得不往下寻找其子类DefaultListableBeanFactory

DefaultListableBeanFactory:不仅继承自 AbstractAutowireCapableBeanFactory还实现了ConfigurableListableBeanFactory;是一个集大成者。上述提到的所有接口他都实现了。这个类是功能最强的。也是 ApplicationContext 中要使用到的。你可能会问创建 bean 的方法已经实现了为什么还需要 DefaultListableBeanFactory,因为创建 bean只是一个功能,如注册 bean相关的资源(BeanDefinition)上述的接口都还没有实现呢。只是我们这里最关注的就是创建 bean 而已。

ApplicationContext 体系结构

ApplicationContext 中涉及的子类等也是非常多的,但是我们目前只关注容器相关的、因此就可以暂时不去关注事件发布、生命周期等。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

这就是其源码,要是一个一个子类去看,那估计得花个一两个月才能理清楚。这个类的主要作用就是获取bean、资源加载、事件发布、国际化以及父子层级。

资源加载体现在:

public interface ResourcePatternResolver extends ResourceLoader {
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
    Resource[] getResources(String locationPattern) throws IOException;
}

这个类的实现类不用看也应当知晓是把我们配置再 xml 或者其他地方的资源加载成 BeanDefinition

ConfigurableApplicationContext:继承ApplicationContext,看命名就知道这是和配置相关的,这个类提供了配置 ApplicationContext 的能力、同时还继承了 LifeCycle 接口。

AbstractApplicationContext:抽象类实现 ConfigurableApplicationContext 、对 ApplicationContext 的方法做了实现、同时定义了 refresh() 方法,这个方法定义了容器刷新的步骤、提供了 postProcessBeanFactoryonRefresh 等模版方法、实现了事件发布等方法。内部还持有 BeanFactory。是一个非常重要的类。

AbstractRefreshableApplicationContext:看名知意、实现了如何刷新容器。

AbstractXmlApplicationContext:实现了解析 xml 方法,从 xml 中解析出 BeanDefinition

ClassPathXmlApplicationContext / FileSystemXmlApplicationContext :这两个就不用多说了一个是从 classpath 中加载xml 一个是从文件系统中加载 xml