【Spring】 BeanFactory 和 ApplicationContext 体系结构

当我们在进行 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项目中的容器配置示例:

xml 复制代码
<!-- 父容器 -->
<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 销毁。

子类需要实现的主要模板方法是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 中涉及的子类等也是非常多的,但是我们目前只关注容器相关的、因此就可以暂时不去关注事件发布、生命周期等。

java 复制代码
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

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

资源加载体现在:

java 复制代码
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

相关推荐
天天喝旺仔22 分钟前
Docker 镜像瘦身实战:多阶段构建把体积缩小 90%
运维·后端·ci/cd·docker·云原生·容器·性能优化
Lost of 程序猿27 分钟前
ASP.NET Core Saga 分布式事务深度实战:备件采购跨服务长流程,如何保证“要么全成,要么全回“
分布式·后端·asp.net
卷无止境30 分钟前
Coding Agent 里的上下文 Compact,到底在压缩什么
后端·python
爱勇宝36 分钟前
公司没给活干,却因为员工看手机把人开了:法院判赔11万元
前端·后端·程序员
2601_9620710041 分钟前
【Java EE】SpringBoot的创建与简单使用
spring boot·后端·java-ee
Csvn42 分钟前
🐍 Day 7:文件 I/O 实战
后端
摇滚侠1 小时前
《SpringBoot 3:入门与应用实战》第 9 章 使用 WebMvc 开发进阶 阅读笔记 24
spring boot·笔记·后端
yunwei371 小时前
AgentCgroup:当 AI Agent 遇到操作系统资源
linux·人工智能·后端
元界metalite1 小时前
SpringBoot开发企业后台-操作日志记录的最佳实践
后端