【从零构建Spring|第六节】 应用上下文, 自动识别, 资源加载, 扩展机制的实现
【从零构建Spring|第六节】 应用上下文, 自动识别, 资源加载, 扩展机制的实现
Shio【从零构建Spring|第六节】 应用上下文, 自动识别, 资源加载, 扩展机制的实现
前言
提示
说到spring容器,有的同学可能知道指的是BeanFactory,有的可能说是ApplicationContext,其实这二者都是容器类。
BeanFactory是底层基础类,位于spring-beans模块中,而ApplicationContext位于spring-context模块,是对BeanFactory的装饰,即包含一个BeanFactory实例。
ApplicationContext 本质上还是调用了 BeanFactory 内部的系列方法实现功能,并拓展了许多别的功能
日志
在手写 Spring 框架过程中,需要不断扩展新的功能,如一个 Bean 的定义和实例化过程前后,是否可以做到支持自定义扩展,能够对 Bean 对象执行一些修改、增强、记录操作?要做到能随时扩展新功能,本身架构就必须设计好,不能把代码写死、耦合
本章节有所改动的内容:
合并获取 BeanFactory、读取配置、注册 Bean 等操作,合并到 Spring 框架上下文中。让面向 Spring 的组件 DefaultListableBeanFactory 尽量不暴露给用户
在 Bean 的定义和初始化过程中插入接口类,以便实现在 Bean 对象从注册到实例化的过程中执行用户的自定义操作
新增两个对 Bean 对象扩展的两个接口,其实也是 Spring 框架中非常具有重量级的两个接口:
BeanFactoryPostProcess
和
BeanPostProcessor
(本质上也是 Bean 的一种,通过
getBeanOfType(Bean(...)Processor.class)
获取
- BeanFactoryPostProcessor,是由 Spring 框架组建提供的容器扩展机制,允许在 Bean 对象注册后但未实例化之前,对 Bean 的定义信息
BeanDefinition
执行修改操作。 - BeanPostProcessor,也是 Spring 提供的扩展机制,不过 BeanPostProcessor 是在 Bean 对象实例化之后修改 Bean 对象(但注册要在实例化前),也可以替换 Bean 对象。这部分与后面要实现的 AOP 有着密切的关系。
- BeanFactoryPostProcessor,是由 Spring 框架组建提供的容器扩展机制,允许在 Bean 对象注册后但未实例化之前,对 Bean 的定义信息
同时如果只是添加这两个接口,不做任何包装,那么对于使用者来说还是非常麻烦的。我们希望于开发 Spring 的上下文操作类,把相应的 XML 加载 、注册、实例化以及新增的修改和扩展都融合进去,让 Spring 可以自动扫描到我们的新增服务,便于用户使用。
Spring 应用上下文和对Bean对象扩展机制的类关系
- 在整个类图中主要体现出来的是关于 Spring 应用上下文以及对 Bean 对象扩展机制的实现。
- 以继承了 ListableBeanFactory 接口的 ApplicationContext 接口开始,扩展出一系列应用上下文的抽象实现类,并最终完成
ClassPathXmlApplicationContext
类的实现。而这个类就是最后交给用户使用的类。 - 同时在实现应用上下文的过程中,通过定义接口:
BeanFactoryPostProcessor
、BeanPostProcessor
两个接口,把关于对 Bean 的扩展机制串联进去了。
设计
工程
BeanPostProcessor
如果我们想在 Spring 容器中完成 bean 实例化、配置以及其他初始化方法前后要添加一些自己逻辑处理。我们需要定义一个或多个 BeanPostProcessor 接口实现类,然后注册到 Spring IoC 容器中。它本身也是属于一种 Bean,可以注册到 Spring IoC ,通过 BeanFactory 的 getBeanOfType() 获取 bean 后置处理器集合
public interface BeanPostProcessor { |
由API可以看出:
- 后置处理器的
postProcessorBeforeInitailization
方法是在 bean 实例化,依赖注入之后及自定义初始化方法(例如:配置文件中bean标签添加init-method属性指定Java类中初始化方法、@PostConstruct
注解指定初始化方法,Java类实现InitailztingBean
接口)之前调用 - 后置处理器的 postProcessorAfterInitailization 方法是在bean实例化、依赖注入及自定义初始化方法之后调用
一个后置处理器的 xml 写法:
|
注意
BeanFactory 和 ApplicationContext 两个容器对待bean的后置处理器稍微有些不同。
ApplicationContext 容器会自动检测 Spring 配置文件中那些 bean (因为那些 bean 所对应的Java类实现了 BeanPostProcessor 接口),并自动把它们注册为后置处理器。在创建 bean 过程中调用它们,所以部署一个后置处理器跟普通的bean没有什么太大区别。
BeanFactory容器注册bean后置处理器时必须通过代码显示的注册,在IoC容器继承体系中的ConfigurableBeanFactory接口中定义了注册方法
ConfigurableBeanFactory 接口
/** |
Spring 调用多个 BeanPostProcessor
我们可以在 Spring 配置文件中添加多个 BeanPostProcessor(后置处理器) 接口实现类,在默认情况下Spring 容器会根据后置处理器的定义顺序来依次调用。
Spring配置文件:
|
postProcessor、postProcessorB 两个 BeanPostProcessor 的实现类(以 ApplicationContext 容器举例),按照 xml 的书写顺序依次注册执行,先 postProcessor 后 postProcessorB
当然 Spring 也支持通过 order 指定后置处理器调用顺序(需要 Order 接口,当前并没有书写此逻辑),通过让BeanPostProcessor接口实现类实现Ordered接口getOrder方法,该方法返回一整数,默认值为 0,优先级最高,值越大优先级越低
BeanFactoryPostProcessor
Spring 中 BeanFactoryPostProcessor 和 BeanPostProcessor 都是 Spring 初始化 bean 时对外暴露的扩展接口,两个接口名字听起来很像,但实际作用和使用场景却不同
Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化之前读取 Bean 的定义(也称配置元数据),并可以修改它们。可定义多个 BeanFactoryPostProcessor ,通过设置 order 属性(需要 Order 接口,当前暂时没书写此逻辑)来确定各个BeanFactoryPostProcessor执行顺序。
注册一个 BeanFactoryPostProcessor 实例需要定义一个 Java 类来实现 BeanFactoryPostProcessor 接口,并重写该接口的 postProcessorBeanFactory 方法。通过 beanFactory 可以获取 bean 的定义信息,并可以修改 bean 的定义信息。这点是和 BeanPostProcessor 最大区别.
/** |
核心方法 refresh 实现
AbstractApplicationContext,只实现核心功能 refresh()
- AbstractApplicationContext 继承 DefaultResourceLoader 是为了处理
spring.xml
配置资源的加载。 - 之后是在 refresh() 定义实现过程
- 另外把定义出来的抽象方法,refreshBeanFactory()、getBeanFactory() 由后面的继承此抽象类的其他抽象类实现。
抽象类 AbstractRefreshableApplicationContext
实现 refresh() 定义的模板方法:
:warning:
这里的 beanFactory 算是一个非常精妙的操作了,注意到他的创建在 AbstractRefreshableApplicationContext
而不是 AbstractApplicationContext
了吗?
按照我们一般人写逻辑,直接把工厂的创建放在和调用一个位置了。因为理解起来直观呀!但是代码整体不美观,且功能耦合。
Spring 将工厂的创建放在另一个抽象类中,且创建的是最基层的 beanFactroy —— DefaultListableBeanFactory。它拥有注册和获取 Bean 两个最基本的方法,也是 Bean 工厂最本质的功能。在这个抽象类里,读取配置文件,注册 Bean,并将这个注册好的 Factroy 放入内存中供 refresh 调用。而在 refresh 里,又通过向上转型的方式,将 beanFactroy 升级成 ConfigurableBeanFactory 可配置工厂,让这个最基础的工厂具有获取添加后置处理器的功能。
我们可以从 Spring 框架学会一点,最基础最核心最本质的功能在最顶级接口定义,实现类却位于最底层,中间都由接口不断的继承。这样的好处是实现类可以通过先上转型的方式获取到更多更全的内容。一方面让各个接口各司其职,不让代码耦合写死混乱;另一方面可维护性、可扩展性高,只要书写好文档说明,代码迭代的就会非常轻松。例如面向 Spring 所开发的 BeanFactory 系列组件、以及面向用户所设计的 ConfigurableBeanFactory 可配置工厂。
测试
|
ApplicationContext 功能
- ApplicationEventPublisher:Ioc 事件派发器
- MessageSource:国际化解析器
- BeanFactory:Bean 工厂 –> 组合自动装配
- 资源解析
是用户能看见的 Bean 工厂,但 ApplicationContext 只是接口,我们都是调用其子类的方法来完成需求
流程分析
应用上下文容器的执行细节
- 启动项目,Spring 加载 applicationContext 应用上下文容器,进入 refresh() 核心步骤
- 创建 BeanFactory,读取 XML 配置文件(集成在 ApplicationContext)
- 通过 refresh() 中的 refreshBeanFactory() 将创建 Bean 工厂 (创建的是默认最基础工厂 DefaultListableBeanFactory 其具有 注册 bean 和 获取 bean 的功能) 和读取 xml 资源文件
- 指定资源路径供 Spring IoC 中的组件 XmlBeanDefinitionReader 读取信息将其加载至 BeanDefinition 。注册好的 beanFactory 放入内存里
- 提升 beanFactory ,通过自动转换的方式,由 DefaultListableBeanFactory 升级为 ConfigurableListableBeanFactory ,让工厂多了可配置工厂接口定义的方法:预先实例化单例 bean、增添后置处理器 addBeanPostProcessor
- 在 bean 实例化之前,Spring 注册 PostProcessor 后置处理器
- invokeBeanFactoryPostProcessors 通过 getBeanOfType 获取用户自定义的所有 MyBeanFactoryPostProcessors 集合(因为 BeanFactoryPostProcessor 本质也是 bean 的一种)从而修改 beanDefinition 配置元数据
- registerBeanPostProcessors 通过 getBeanOfType 获取用户自定义的所有 MyBeanPostProcessors 集合
- beanFactory.preInstantiateSingletons() 预处理实例化 XML 书写的所有 Bean
应用上下文容器完成了数据的读取,创建,bean对象的实例化
创建 Bean 的执行细节
- bean 通过 Cgilb(字节码增强) 或者 JDK 自带的手段(反射手段)和 BeanDefinition 中所定义的配置元数据实例化 Bean 对象
- 往实例化 Bean 对象填充属性,如果是引用,递归的获取引用的 Bean 实例
- 执行 BeanPostProcessor 后置处理器的逻辑,在初始化之前需要做操作 Before
- 初始化 Bean,目前未实现,为空逻辑
- 执行 BeanPostProcessor 后置处理器的逻辑,在初始化之后需要做操作 After
- 将注册好的 Bean 存入单例缓存