当我们在进行 Java 开发的时候、是否遇见过如下的场景:
- 当我们无法注入某个对象时,我们不得不写一个工具类并注入
ApplicationContext对象、通过其getBean()方法获取我们需要的对象,如在使用easyexcel时、需要在其Listener进行数据库查询操作时、注入具体的Dao操作对象。- 项目中某两个模块在开发前期没有设计好出现
service层互相依赖导致项目无法正常启动,如用户模块和部门模块,有时需要部门ID查询所有的职员、有时又需要从用户模块反查部门信息;此时为什么使用@Lazy注解就可以正常启动。- 常见的
Spring面试题都能看见的事务失效、循环依赖、Bean的生命周期或者创建过程等。- 当我们启动项目需要事先加载一些资源时为什么有的会使用
@PostConstruct、有的采用实现ApplicationRunner?
为了彻底弄明白上述问题以及更好的处理日常开发中遇到的问题,我便较为深入和系统的学习了 Spring 相关的知识。基于此、便有了打算将自己对 Spring 的理解转换成自己的语言和文字的想法。主要分为如下板块:
BeanFactory和Application的体系结构BeanFactory和Bean的扩展接口Bean的创建过程Aop的实现和Spring动态代理对象的创建Spring的事件发布机制- 一些知识点的补充
本文是第一篇,主要内容为了解 BeanFactory 和 ApplicationContext 体系结构。了解这一部分内容之后,能够较好的理解单一职责原则。
当我们准备换工作或者刷面试资料时,有时候会看见这么一句话 BeanFactory 是清水房、ApplicationContext 是精装修。他们之间到底是什么关系那么就首先从 BeanFactory 谈起。
BeanFactory 体系结构
BeanFactory:是 Spring 容器的根接口、主要定义了获取单个 bean 的方法以及 FactoryBean的前缀FACTORY_BEAN_PREFIX = "&";BeanFactory 有三个直接子接口:
ListableBeanFactoryHierarchicalBeanFactoryAutowireCapableBeanFactory
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 的能力,如setParentBeanFactory 和addBeanPostProcessor。除此之外还继承了DefaultSingletonBeanRegistry;这个类提供了三级缓存和 bean 的注册;
ConfigurableListableBeanFactory:继承自 ConfigurableBeanFactory、ListableBeanFactory、AutowireCapableBeanFactory、这三个接口、不仅是在 ConfigurableBeanFactory 的基础上增加了枚举能力(批量获取对象的能力)
这两个接口主要是用于框架内部使用,普通的业务开发不建议使用。
上述的都是接口,在这里可能会有疑问为什么需要定义这么多接口呢?放在一个接口不行吗?实际上每一个接口都不是多余的。每一个接口都是对一个能力的抽象,如最基础的 BeanFactory、批量获取的 ListableBeanFactory、可以配置 BeanFactory 的ConfigurableBeanFactory 和 ConfigurableListableBeanFactory。这体现了单一职责原则。
AbstractBeanFactory:看名字就应当清楚这是一个抽象类,是对 BeanFactory 的基础实现,提供了单例缓存、单例/原型判定、FactoryBean 处理、别名处理、子 Bean 定义的 Bean 定义合并,以及 Bean 销毁。

子类需要实现的主要模板方法是getBeanDefinition和 createBean,分别用于根据给定的 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() 方法,这个方法定义了容器刷新的步骤、提供了 postProcessBeanFactory 和 onRefresh 等模版方法、实现了事件发布等方法。内部还持有 BeanFactory。是一个非常重要的类。
AbstractRefreshableApplicationContext:看名知意、实现了如何刷新容器。
AbstractXmlApplicationContext:实现了解析 xml 方法,从 xml 中解析出 BeanDefinition。
ClassPathXmlApplicationContext / FileSystemXmlApplicationContext :这两个就不用多说了一个是从 classpath 中加载xml 一个是从文件系统中加载 xml。