PS * 本文代码基本为伪代码,注释为个人理解,水平有限,如有谬误,感谢指正。

关于spring的容器,除了BeanFactory以及它的默认实现类XmlBeanFactory之外。

Spring还提供了 ApplicationContext ,

它用于对 BeanFactory的拓展。

本文入口:

    ApplicationContext bf = new ClassPathXmlApplicationContext("bean.xml"");

核心代码:

	public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) { prepareRefresh();//解析预备 刷新上下文环境 例如对系统属性或者环境变量进行校验和准备 // 初始化 beanFactory 并读取xml 配置文件 , 此函数过后即拥有了 BeanFactory的全部功能
// 向下转型 实际持有 它的子类: DefaultListableBeanFactory 类型的对象
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // ****#### DefaultListableBeanFactory <- beanFactory
// 功能拓展
// beanFactory 初始化 <功能填充> 复用 BeanFactory 中的 配置文件读取、解析 以及其他功能
prepareBeanFactory(beanFactory); try { postProcessBeanFactory(beanFactory);// 钩子函数,由子类实现 后处理器 // 激活各种 BeanFactory 处理器
// postProcessor 后处理器
// ** 激活 ** 注册的 各种 BeanFactoryPostProcessor
// **激活** 且 **注册**
invokeBeanFactoryPostProcessors(beanFactory); // ** 注册 ** BeanPostProcessors ,用于拦截 Bean 的创建
// 《后处理器》,仅仅注册,在getBean方法调用时才会实际触发 《不激活》
// 因为仅仅注册,所以不需要考虑 硬编码方式的后处理器
// 对于硬编码方式的后处理器 仅仅在 getBean时被调用
registerBeanPostProcessors(beanFactory);
// 注册最终调用 AbstractBeanFactory.addBeanPostProcessor()
// AbstractBeanFactory <== AbstractAutowireCapableBeanFactory <== DefaultListableBeanFactory // 为上下文初始化message 源 ,即不同语言的消息体(国际化处理)
initMessageSource(); // 初始化 应用消息 广播器,并放入 "applicationEventMulticaster" bean 中
initApplicationEventMulticaster(); // 留个子类来初始化其它的 bean 《钩子函数》
onRefresh(); // 在所有注册的bean中,查找 Listener-bean , 注册到消息广播器中
registerListeners(); // 初始化 非延迟加载单例
finishBeanFactoryInitialization(beanFactory); // 完成刷新过程,通知生命周期处理器 lifecycleProcessor 刷新过程
// 发出 ContextRefreshEvent 通知别人
finishRefresh();
}
catch (BeansException ex) {
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}

一、解析预备 刷新上下文环境 例如对系统属性或者环境变量进行校验和准备

  • 定义钩子函数:initPropertySources() 当需要校验时由子类实现该方法<模板方法模式>
  • 通过 getEnvironment().validateRequiredProperties() 校验
protected void prepareRefresh() {

		//  子类实现该函数,并设置需要校验的 内容
initPropertySources();// 钩子函数 初始化上下文环境中的任何占位符属性资源 // 验证需要的属性文件是否已经 放入环境中 《《《 initPropertySources 是否完成工作 ??
getEnvironment().validateRequiredProperties();
}

二、初始化 BeanFactory 并进行 Xml 配置文件的读取

  • 已知的是ApplicationContext 是对BeanFactory的 拓展,经过这一步之后,ApplicationContext 将拥有 BeanFactory 的全部功能;

  • AbstractRefreshableApplicationContext 中 实现的方法 refreshBeanFactory() 会产生一个默认的 DefaultListableBeanFactory 对象,

  • 并加载类的 xml 配置 ,最终 ApplicationContext 将持有该 DefaultListableBeanFactory 对象;因此说它拥有 BeanFactory 的所有功能。

@Override
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {// 已有,不再重复该逻辑
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());// 为了序列化指定ID // 定制 beanFactory 包括是否允许覆盖同名称不同定义的对象、循环依赖
// 设置 @AutoWired 和 @Qualifier 的注解解析器 QualifierAnnotationAutowireCandidateResolver
customizeBeanFactory(beanFactory);
// 初始化 DocumentReader 并进行 xml 读取、解析
loadBeanDefinitions(beanFactory);// 类 AbstractXmlApplicationContext 提供实现
this.beanFactory = beanFactory;// 记录到全局变量
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

三、对BeanFactory 各种功能填充

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {

  1. 定制 BeanFactory 设置 beanFactory 的类加载器 为当前的 context的 类加载器

  2. 增加对SpEL 语言的支持

  • 注册表达式语言SpEL解析器/处理器, 它主要是在依赖解析注入bean的时候、完成bean的初始化和属性获取后的属性填充的时候调用。

  • 例如解析: #{bean.filed}

    loadBean[ BeanFactory.getBean(beanName); ] 过程中的属性注入注入环节:applyPropertyValues,

    会调用方法 BeanDefinitionValueResolver.resolveValueIfNecessary() 解析上述定义的参数

  1. 增加 属性注册编辑器
  • 添加默认的propertyEditor,它主要是:对bean的属性等设置进行管理,的一个工具

例如在 XML 配置中定义

<property name="currentDate">
<value>2021-05-03</value>
</property>
<property name="name">
<value>张二狗</value>
</property>

在 bean 中定义:

private Date currentDate;
private String name;

当spring 进行注入的时候,可以把普通属性name注入进来,但是却不能识别bean中定义的Date类型currentDate;

有两种解决方法:

a. 使用自定义属性编辑器即可,需要继承 类: Property|EditorSupport 并重写 setAsText() 方法;

b. 使用spring 自带的属性编辑器:CustomDateEditor,

设置属性注册编辑器 AbstractBeanFactory.addPropertyEditorRegistrar();

  • spring自带属性编辑器,使用案例详见: AbstractBeanFactory.initBeanWrapper() -> registerCustomEditors();

    -> ResourceEditorRegistrar.registerCustomEditor() 注册常用的 属性编辑器
  1. 增加 ApplicationContextAwareProcessor 处理器,该后处理器用于对 容器的补充/增强,而非普通bean的后处理器

在init-method的前后,将调用该处理器的postProcessBeforeInitialization()方法 和postProcessAfterInitialization()方法;

  1. 设置 忽略依赖

当 Spring 将ApplicationContextAwareProcessor 注册后,在 invokeAwareInterfaces() 方法中 间接调用的Aware 类已经不是普通的 bean 了,

如:ResourceLoaderAware,所以需要在spring进行bean的依赖注入的时候忽略掉它们。

  1. 注册依赖

同样spring 也提供了 注册依赖的功能

//  设置几个忽略自动装配的特殊规则
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

当解析到对类型 BeanFactory.class 的依赖时,会直接将它的实例 beanFactory 注入。

四、激活以及注册各种 BeanFactoryPostProcessor 后处理器

PostProcessorRegistrationDelegate
.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
  • 后处理器的作用范围是容器级的,仅仅对当前容器中的 bean 生效;

  • 需要激活 以及 注册(提取并调用)

  • 还需要考虑 各种后处理器是否 实现了排序接口

  • BeanFactoryPostProcessor 可以对 bean 定义的元数据进行处理,

    可以说Spring 容器 允许 BeanFactoryPostProcessor , 在实际实例化任意bean之前读取配置的元数据并进行处理。

  • 可以分为xml 文件配置的 BeanFactoryPostProcessor 后处理器 和 硬编码类型的 BeanFactoryPostProcessor 后处理器。

五、注册后处理器 BeanPostProcessor

  • 仅仅对 BeanPostProcessor 类型的后处理器进行注册,实际调用发生在 getBean(beanName); 实例化bean的时候

  • 类似对 BeanFactoryPostProcessor 的处理,区别是 BeanPostProcessor 由于只需要注册不需要激活,所以它只处理配置文件中配置的 BeanPostProcessor ,而不处理硬编码类型的 BeanPostProcessor

后续环节

后续环节还包括:

  • 为上下文初始化message 源 ,即不同语言的消息体(国际化处理)

  • 初始化 应用消息 广播器,并放入 "applicationEventMulticaster" bean 中

  • 在所有注册的bean中,查找 Listener-bean , 注册到消息广播器中

  • 初始化 非延迟加载单例

  • 完成刷新过程,通知生命周期处理器 lifecycleProcessor 刷新过程

    。。。。 等等

由于逻辑并不复杂,不再记录。

-- 本文仅为个人学习spring源码后的理解。

Spring源码之容器的功能拓展-ApplicationContext的更多相关文章

  1. Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签

    写在前面 上文Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作中Spring对XML解析后创建了对应的Docum ...

  2. Spring源码学习-容器BeanFactory(二) BeanDefinition的创建-解析前BeanDefinition的前置操作

    写在前面 上文 Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件主要讲Spring容器创建时通过XmlBeanDefinitionReader读 ...

  3. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  4. Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md

    写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...

  5. Spring源码系列——容器的启动过程(一)

    一. 前言 Spring家族特别庞大,对于开发人员而言,要想全面征服Spring家族,得花费不少的力气.俗话说,打蛇打七寸,那么Spring家族的"七寸"是什么呢?我心目中的答案一 ...

  6. Spring源码系列 — 容器Extend Point(一)

    前言 前文介绍了Spring中的BeanDefinition的细节,随着Spring的启动流程,这节我们介绍Spring的后续处理过程 - Spring的扩展点: BeanFactoryPostPro ...

  7. Spring源码学习-容器BeanFactory(五) Bean的创建-探寻Bean的新生之路

    写在前面 上面四篇文章讲了Spring是如何将配置文件一步一步转化为BeanDefinition的整个流程,下面就到了正式创建Bean对象实例的环节了,我们一起继续学习吧. 2.初始化Bean对象实例 ...

  8. spring源码-增强容器xml解析-3.1

    一.ApplicationContext的xml解析工作是通过ClassPathXmlApplicationContext来实现的,其实看过ClassPathXmlApplicationContext ...

  9. spring源码深度解析-2功能扩展

    容器功能的扩展ApplicationContext用于扩展BeanFactory中现有的功能.究竟多出了哪些功能,进一步探索.写法上:BeanFactory bf = new XmlBeanFacto ...

  10. spring源码-ioc容器周期

    Spring容器的refresh 创建刷新:   1-prepareRefresh刷新前的预处理: initPropertySources 初始化一些属性配置,原来是空的,子类自定义的属性设置方法 g ...

随机推荐

  1. SQLServer备份恢复的总结-同名恢复与异名恢复

    SQLServer备份恢复的总结 前言 GUI 搞一把. 命令行太多了也没人看 自己还能省点心 备份 备份数据库建议一定要选择: 备份选项中的 压缩->压缩备份 历史经验一个7.6G的数据库能够 ...

  2. Chrony 的学习与使用

    Chrony 的学习与使用 背景 之前捯饬 ntp 发现很麻烦, 经常容易弄错了. 昨天处理文件精确时间时 想到了时间同步. 发现只有自己总结的ntpdate 但是还没有 chronyd相关的总结 本 ...

  3. [转帖]性能测试工具netperf安装使用

    https://blog.51cto.com/dingtongxue1990/1853714 netperf工具使用 一.安装 1,下载 liunx下载地址:ftp://ftp.netperf.org ...

  4. [转帖]ES集群开启X-pack认证

    https://www.cnblogs.com/jclty/p/12913996.html 1.下载 1 # wget https://artifacts.elastic.co/downloads/e ...

  5. [转帖]configure 各种配置

    https://api.dandelioncloud.cn/article/details/1487329970564485121 -build=编译该软件所使用的平台 -host=该软件将运行的平台 ...

  6. runc网络与systemd管理runc应用

    1. 创建网络命名空间 ip netns add <ns> # ns是自定义网络空间名 # 例如: ip netns add haproxy # 创建一个给 haproxy使用的网络命名空 ...

  7. 神奇的 SQL ,同时实现小计与合计,阁下该如何应对

    开心一刻 今天,小区有个很漂亮的姑娘出嫁 我对儿子说:你要好好学习,认真写作业,以后才能娶到这么漂亮的老婆 儿子好像听明白了,思考了一会,默默的收起了作业本 然后如释重负的跟我说到:爸,我以后还是不娶 ...

  8. 使用Docker单机部署Ceph

    安装Docker过程参考:https://www.cnblogs.com/hackyo/p/9280042.html 1. 创建Ceph专用网络 sudo docker network create ...

  9. Android 开机流程介绍

    目录 一.目的 二.环境 三.相关概念 3.1 Android平台架构 3.2 Android启动架构 3.3 zImage 3.4 RAMDISK 3.5 RC文件 四.详细设计 4.1 Boot ...

  10. 神经网络优化篇:详解如何训练一个 Softmax 分类器(Training a Softmax classifier)

    如何训练一个 Softmax 分类器 回忆一下之前举的的例子,输出层计算出的\(z^{[l]}\)如下,\(z^{[l]} = \begin{bmatrix} 5 \\ 2 \\ - 1 \\ 3 \ ...