回顾

上文 其实我们已经实现了一个简单的 BeanFactory 它具的功能有

  • 注册 Bean 到容器,通过限定名获取 Bean
  • 可以拦截 Bean 初始化前后的处理
  • 可以在 Bean 属性注入后和即将销毁时做一些逻辑处理
  • 解决了循环依赖

其实总结起来它实现的方法就是:加载 Bean 定义、实例化 Bean,很简单吧

Bean 在 spring 中的完整生命周期,可以自行查看spring 的 BeanFactory 接口,它在最上面的注释有详细说明。

但实际的应用场景除了这个主要 Bean 管理外,还有一些消息广播、国际化、事件监听等

本文内容

本文想分析下 ApplicationContextrefresh 过程,先来个总结性的过程,以便后续一堆枯燥的源码分析

  • 初始阶段:准备工作

  • 第一步,我们先和得到一个 BeanFactory ,这里初始化的是 DefaultListableBeanFactory

  • 第二步,我们需要配置这个 BeanFactory 像 spel 表达式解析器、Environment 都要配置到工厂里面,以便后续的处理

  • 第三步,我们允许添加 BeanFactoryProcessor 来修改 BeanFactory 中的 Bean ,常用的子类 BeanDefinitionRegistryPostProcessor 它可以往容器中添加 Bean 定义

    像 @Import、@PropertySource、@ComponentScan、@ImportResource、@Bean methods 还有 tk.mybatis 都是靠它来注入自定义的 Bean 定义 的,不清楚什么是 bean 定义看我的 上篇文章

  • 第五步,当然是 回调 上面收集到的所有的 BeanFactoryProcessor

  • 第六步,回调 所有收集到的 BeanPostProcessor,这些 processor 是已经加到容器中的,如果你看到这里,不懂什么是 BeanPostProcessor 请回头看 上文 的 bean 的生命周期,不清楚回调那就没办法了,你还不够经验看这篇文章

  • 第七步,国际化初始化(对于主体来说,这算是支线,因为大部分情况下我们都不是写的国际项目)

  • 第八步,初始化事件广播器。事件:常见的如页面加载完成有 onLoad 事件,应用刚启动有 onLaunch 事件,项目中用得不多,可能是 spring 内部用得多吧

  • 第九步,留一个回调给子类来初始化特别的 bean ,默认空的

  • 第十步,注册监听器(所有实现了 ApplicationListener 的类),并把之前的一些事件发布一下

  • 第十一步,基本上是单例实例化,基本上我们项目中的类都是在这个方法中实例化的

  • 结束阶段:清理工作

看这个过程的好处

  • 熟悉 IOC 容器初始化的流程,可以把 spring 用得更好
  • 一些像 spel 工具类性质的东西可以直接拿过来用,没必要引第三方库或重复造轮子

正文

不会把每一步都细品,只会对一些关键的步骤进行解读,像如何加载 Bean 定义,读 xml 和读注解的,读者可以自行研读,本文讨论几个关键的 processor

我们都知道,Bean 的生命周期中,可以添加 BeanPostProcessor 在 bean 的初始化前和初始化后做一些处理,同样的在 BeanFactory 也有一个 BeanFactoryPostProcessor 允许你在 BeanFactory 初始化后,修改 BeanFactory。

样例:

我们看一下 PropertyPlaceholderConfigurer xml 配置时代的产物, 它的继承结构如下

PropertyResourceConfigurer implements BeanFactoryPostProcessor
|-PlaceholderConfigurerSupport
|-PropertyPlaceholderConfigurer

我们通常会在最开始配置这个

<context:property-placeholder location="classpath:jdbc.properties" />

PropertyPlaceholderConfigurer 用于对项目中的 @Value 值进行处理,把值注入进属性中,下面看下它如何实现的

查看关键方法 PropertyResourceConfigurer.postProcessBeanFactory,分为三步

Properties mergedProps = mergeProperties();		// 从 location 中加载属性

// Convert the merged properties, if necessary.
convertProperties(mergedProps); //什么都没干,一个模板方法,可用于密码加密,自定义属性转换 // Let the subclass process the properties.
processProperties(beanFactory, mergedProps); // 真正处理属性的地方,解析 spel 表达式 ${}

真正的处理过程使用了访问者模式在 BeanDefinitionVisitor 中。

springboot 又是怎么处理的呢,springboot 是固定了配置文件 application.properties ,而不再使用 location 来配置路径,它使用了 PropertySourcesPlaceholderConfigurer 来解析配置 ,它同样继承自 PlaceholderConfigurerSupport 并且重写了 postProcessBeanFactory 关键方法,在最后面使用了

PropertySourcesPropertyResolver 进行了属性解析。

使用方式可以在 这篇文章 看到

BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor 它的参数是 BeanDefinitionRegistry 一个 Bean 定义 的注册器,它可以做些什么呢 ,查看它的方法,可以对 bean 定义做增删改查,厉害吧。

registerBeanDefinition
removeBeanDefinition
getBeanDefinition
containsBeanDefinition
getBeanDefinitionNames
getBeanDefinitionCount
isBeanNameInUse

样例一:

ConfigurationClassPostProcessor 中,它实现了 BeanDefinitionRegistryPostProcessor ,它使用 ConfigurationClassParser 来解析项目中配置的 @Configuration 类,然后使用 ConfigurationClassBeanDefinitionReader 把解析到的类加载到 Bean 定义

具体细节查看 这篇文章

样例二:

在 tkmybatis 的 MapperScannerConfigurer 中,也实现了 BeanDefinitionRegistryPostProcessor ,它使用 ClassPathMapperScanner 来扫描 Mapper 类,添加进容器

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner

划重点

其实总结来说,refresh 的过程只是给你提供了一个大致的执行框架,具体的处理还是在一些 processor,listener 中,网上有大部分源码解读都拿这个 refresh 过程做大篇幅的讲解,其实没多大必要,就像 servlet 的生命周期一样,了解其执行过程,但剩下的处理都是子类去具现化的。

分享一篇说得不错的文章

BeanPostProcessor和BeanFactoryPostProcessor浅析以及在spring初始化中回调

一点小推广

创作不易,希望可以支持下我的开源软件,及我的小工具,欢迎来 gitee 点星,fork ,提 bug 。

Excel 通用导入导出,支持 Excel 公式

博客地址:https://blog.csdn.net/sanri1993/article/details/100601578

gitee:https://gitee.com/sanri/sanri-excel-poi

使用模板代码 ,从数据库生成代码 ,及一些项目中经常可以用到的小工具

博客地址:https://blog.csdn.net/sanri1993/article/details/98664034

gitee:https://gitee.com/sanri/sanri-tools-maven

ApplicationContext refresh 过程及一些重要的 processor 解析的更多相关文章

  1. springboot启动流程(七)ioc容器refresh过程(上篇)

    所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 正文 在前面的几篇文章中,我们看到Environment创建.application配置文件的 ...

  2. springboot启动流程(八)ioc容器refresh过程(下篇)

    所有文章 https://www.cnblogs.com/lay2017/p/11478237.html 正文 上一篇文章,我们知道了解析过程将从解析main方法所在的主类开始.在文章的最后我们稍微看 ...

  3. Java虚拟机JVM学习03 连接过程:验证、准备、解析

    Java虚拟机JVM学习03 连接过程:验证.准备.解析 类被加载后,就进入连接阶段. 连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去. 连接阶段三个步骤:验证.准备和解析. 类 ...

  4. spring5 源码深度解析-----ApplicationContext容器refresh过程

    在之前的博文中我们一直以BeanFactory接口以及它的默认实现类XmlBeanFactory为例进行分析,但是Spring中还提供了另一个接口ApplicationContext,用于扩展Bean ...

  5. spring源码学习之:spring容器的applicationContext启动过程

    Spring 容器像一台构造精妙的机器,我们通过配置文件向机器传达控制信息,机器就能够按照设定的模式进行工作.如果我们将Spring容器比喻为一辆汽车,可以将 BeanFactory看成汽车的发动机, ...

  6. DM6437 dsp系列之启动过程全析(2)—AIS文件解析

    本文均属自己阅读源码的点滴总结,转账请注明出处谢谢. 欢迎和大家交流.qq:1037701636 email: gzzaigcn2009@163.com,gzzaigcn2012@gmail.com ...

  7. Tomcat启动过程(二):EndPoint解析

    EndPoint提供基础的网络IO服务,用来实现网络连接和控制,它是服务器对外I/O操作的接入点.主要任务是管理对外的socket连接,同时将建立好的socket连接交到合适的工作线程中去.里面两个主 ...

  8. 【分布式】Zookeeper的Leader选举-选举过程介绍(经典的Paxos算法解析)

    一.前言 前面学习了Zookeeper服务端的相关细节,其中对于集群启动而言,很重要的一部分就是Leader选举,接着就开始深入学习Leader选举. 二.Leader选举 2.1 Leader选举概 ...

  9. (五)SpringBoot启动过程的分析-刷新ApplicationContext

    -- 以下内容均基于2.1.8.RELEASE版本 紧接着上一篇[(四)SpringBoot启动过程的分析-预处理ApplicationContext] (https://www.cnblogs.co ...

随机推荐

  1. Nginx的基本安装配置

    Centos7安装nginx 升级nginx 升级可能遇到问题(我没有遇到, 参考的另一篇文章描述的) 检查nginx版本, 确认安装成功 nginx配置文件 虚拟主机配置 配置文件中可以用的全局变量 ...

  2. MYSQL SQL语句 之 select

    select语句在数据库操作中是操作频率最高的语句,使用方式也是多种多样,它的基本功能是:从表中选取数据,结果存储在一个结果集中.可以联合where,and,or,Order By,distinct, ...

  3. SEER流量众筹模块开发测试网络及使用文档发布

    SEER利用区块链奖励机制,可解决传统体育赛事痛点,提高行业运转效率.比如提高赛事方收入,让观众自由选择想看的比赛,给予赛事众筹的参与者贡献影响力,使其获得由智能合约量化的激励等.此功能可广泛应用于包 ...

  4. STM32SPI连续读写多个字节会产生时间间隔

    最近在做一个音频芯片的项目用到SPI接口配置寄存器,发现只要连续两次向从机发送(接收)帧,当STM32处于主机模式时,这两帧数据中间会产生一个时钟的间隙. 起初我想能不能利用状态标志来去除间隙,后来怎 ...

  5. Windows下计划任务的使用

    0x01 前言 在渗透测试中,尤其是域渗透,常常会用到Windows系统的计划任务,一是用于远程启动程序,二是用于程序的自启动 那么,计划任务具体有哪些使用技巧呢?是否对权限有要求?一定需要管理员权限 ...

  6. PHP array_udiff_uassoc

    1.函数的参数:返回数组的差集.用定义的函数比较键值和值. 2.函数的参数: @params array $array @params array $array1 ... @params callab ...

  7. [NOIp2013] luogu P1970 花匠

    scy居然开网了. 题目描述 你有一个序列 aaa,你需要保留尽量多的数,使得剩下的数满足以下条件中的一个: ∀x∈[2,n−1]∩N∗\forall x\in[2,n-1]∩\N^*∀x∈[2,n− ...

  8. Linux power supply class(1)_软件架构及API汇整【转】

    1. 前言 power supply class为编写供电设备(power supply,后面简称PSY)的驱动提供了统一的框架,功能包括: 1)抽象PSY设备的共性,向用户空间提供统一的API. 2 ...

  9. POJ 1276 Cash Machine(多重背包的二进制优化)

    题目网址:http://poj.org/problem?id=1276 思路: 很明显是多重背包,把总金额看作是背包的容量. 刚开始是想把单个金额当做一个物品,用三层循环来 转换成01背包来做.T了… ...

  10. windows下cmd组合命令和管道命令

    组合命令:&& 管道命令:|