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. [转帖]TiDB BR 备份至 MinIO S3 实战

    https://tidb.net/blog/3a31d41d#3.%E9%83%A8%E7%BD%B2%20MinIO%20S3%20%E5%8F%8A%E5%A4%87%E4%BB%BD%E6%81 ...

  2. [转帖]nginx 反向代理 URL替换方案

    nginx 提供反向代理服务,日常开发过程中有时候我们需要使用nginx 作为代理服务根据url的不同去访问不同的服务器或者不同端口,如下提供两种方案. 1.直接替换location  匹配部分 1. ...

  3. [转帖]鲲鹏性能优化十板斧——鲲鹏处理器NUMA简介与性能调优五步法

    https://www.cnblogs.com/huaweicloud/p/12166354.html 1.1 鲲鹏处理器NUMA简介 随着现代社会信息化.智能化的飞速发展,越来越多的设备接入互联网. ...

  4. [转帖]深入理解mysql-第十章 mysql查询优化-Explain 详解(上)

    目录 一.初识Explain 二.执行计划-table属性 三.执行计划-id属性 四.执行计划-select_type属性 一条查询语句在经过MySQL查询优化器的各种基于成本和规则的优化会后生成一 ...

  5. 【转帖】Linux 调优篇:虚拟化调优(hugepage 大页内存)* 叁

    一. 大页(HugePages)概念Hugepage的引入二. hugepages相关概念三.Regular Pages 与 HugePagesa.Regular Pagesb.Huge Pages四 ...

  6. CS231N Assignment3 笔记(更新中)

    在这项作业中,将实现语言网络,并将其应用于 COCO 数据集上的图像标题.然后将训练生成对抗网络,生成与训练数据集相似的图像.最后,您将学习自我监督学习,自动学习无标签数据集的视觉表示.本作业的目标如 ...

  7. Ant Design Vue表单验证失败

    表单验证遇见的坑 01 如果你受控数据是这样写的话 const formState= reactive({ youForm:{ youNaNe:'', useSlectValue: '001', da ...

  8. 【小测试】VictoriaMetrics中如何汇总单个time series上的多个data point?

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 问题最终在andy专家的帮助下解决,但是内部的原理还是很迷 ...

  9. 通杀无限 debugger,目前只有 1% 的人知道!

    前言 相信很多小伙伴在进行 web 逆向的时候,都遇到过无限 debugger.最简单的方法,在 debugger 位置,点击行号,右键 Never pause here,永远不在此处断下即可.但是这 ...

  10. 🛠 开源即时通讯(IM)项目OpenIM源码部署指南

    OpenIM的部署涉及多个组件,并支持多种方式,包括源码.Docker和Kubernetes等.这要求在确保不同部署方式之间的兼容性同时,还需有效管理各版本之间的差异.确实,这些都是复杂的问题,涉及到 ...