Spring 源码阅读 04:BeanFactory 初始化

news2024/9/17 9:22:29

本篇要阅读的是 BeanFactory 初始化的部分,也就是 refresh 方法中的这一行方法调用:

// Tell the subclass to refresh the internal bean factory.
// 这里会调用模版方法,通过子类的实现,初始化 BeanFactory 并解析 XML 配置
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

这里调用了 obtainFreshBeanFactory 并得到了一个 ConfigurableListableBeanFactory 类型的 beanFactory。这个方法的作用是初始化 beanFactory ,还会解析 XML 中配置的 Bean。

我们先看源码:

 /**
* Tell the subclass to refresh the internal bean factory.
*  @return  the fresh BeanFactory instance
*  @see  #refreshBeanFactory()
*  @see  #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   refreshBeanFactory();
   return getBeanFactory();
}

这个方法的源码很简单,调用了两个方法,并将第二个方法的结果返回。根据方法的名称可以看到,第一个方法用来刷新(或者初始化)beanFactory,第二个方法用来获取 beanFactory 并返回。

refreshBeanFactory 方法

我们先看 refreshBeanFactory 方法,具体实现过程可以在 AbstractRefreshableApplicationContext 类找到 :

 /**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

从方法的整体流程可以看出,不管当前是不是存在一个 BeanFactory ,这里都会创建一个新的,并进行初始化,我们来逐行分析代码。

销毁已有的 BeanFactory

一开始,会判断当前是否存在一个 BeanFactory,如果有的话,就销毁其中的 Bean,并关闭 BeanFactory。这部分逻辑对应方法体开头的 if 语句块。因为我们分析的是 Spring 容器第一次创建时候的流程,而且之前并没有 BeanFactory 创建的逻辑,所以,目前为止,并不存在一个 BeanFactory,if 语句中的逻辑不会被执行。

实例化新的 BeanFactory

接下来就是 try 语句块中的部分。

首先,执行了 createBeanFactory 方法,创建了一个 BeanFactory。这里的代码也很简单:

protected DefaultListableBeanFactory createBeanFactory() {
   return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

获取到 parent 后,调用构造方法,创建了一个 DefaultListableBeanFactory 的实例并返回。

DefaultListableBeanFactory 这个类型是第一次见到,我们可以大致看一下它的继承关系:

这里的接口实现关系错综复杂,可以先看看留个印象。值得一提的是,它的子类 XmlBeanFactory 是 Spring 中非常重要的一个类,是 Spring 的初级容器。如果使用 XML 配置文件来直接创建 BeanFactory 使用的就是 XmlBeanFactory ,不过现在这个类已经被标记了 @Deprecated

言归正传,回到 refreshBeanFactory 方法中来。创建好 BeanFactory 后,通过 beanFactory.setSerializationId(getId()) 给了它一个 Id,序列化和反序列化的时候用。

对 BeanFactory 进行自定义

接下来,customizeBeanFactory(beanFactory) 执行了 ApplicationContext 对内部的 BeanFactory 的自定义。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
   if (this.allowBeanDefinitionOverriding != null) {
      beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.allowCircularReferences != null) {
      beanFactory.setAllowCircularReferences(this.allowCircularReferences);
   }
}

这里通过给 beanFactory 的成员变量赋值,做了两件事:

  • 允许相同名称的 BeanDefinition 在容器中被覆盖
  • 允许多个 Bean 之间存在循环依赖

这两点涉及到了 BeanDefinition 和 Bean 初始化的流程。

加载 BeanDefinition

最后一个方法调用,loadBeanDefinitions(beanFactory); 执行了 BeanDefinition 的加载。这里的实现逻辑可以在 AbstractXmlApplicationContext 中找到:

 /**
* Loads the bean definitions via an XmlBeanDefinitionReader.
*  @see  org.springframework.beans.factory.xml.XmlBeanDefinitionReader
*  @see  #initBeanDefinitionReader
*  @see  #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
   initBeanDefinitionReader(beanDefinitionReader);
   loadBeanDefinitions(beanDefinitionReader);
}

在这个方法中,首先创建了一个 XmlBeanDefinitionReader 的实例,并对其进行了配置。从它的名字可以知道,这个类用来读取 XML 中的 Bean 定义。

创建好之后,末尾调用了两个方法,我们先看 initBeanDefinitionReader 的源码:

protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
   reader.setValidating(this.validating);
}

方法名告诉我们方法的作用是对 XmlBeanDefinitionReader 进行初始化,这里只是设置了是否对 XML 文件进行验证(默认值是 true),其余什么也没干。我们还注意到,这个方法是 protected 修饰的,因此可以断定,这里是 Spring 留给我们的一个扩展点。我们可以通过重写这个方法,关闭 XML 验证、自定义 XML 的解析器,或者执行其他的一些设置。

最后将 beanDefinitionReader 作为参数,调用了另外一个 loadBeanDefinitions 方法。源码如下:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}

在这里,分别将 configResources 和 configLocations 作为参数,执行了 reader 的 loadBeanDefinitions 方法。不过,getConfigResources 这个方法直接返回了 null,因此,这里的逻辑就是,根据我们创建 ClassPathXmlApplicationContext 时提供的配置文件路径,进行 BeanDefinition 的加载。

~~这里 reader.loadBeanDefinitions 方法加载 BeanDefinition 的逻辑是现在 AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String...) 方法中

完成初始化并赋值

方法的末尾,通过 this.beanFactory = beanFactory; 将之前创建并初始化好的 BeanFactory 赋值给 beanFactory 成员变量。

getBeanFactory 方法

最后我们在回到最开始的 obtainFreshBeanFactory 方法,看一下里面调用的第二个方法 getBeanFactory() 的源码,实现逻辑在 AbstractRefreshableApplicationContext 类中:

@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
   synchronized (this.beanFactoryMonitor) {
      if (this.beanFactory == null) {
         throw new IllegalStateException("BeanFactory not initialized or already closed - " +
               "call 'refresh' before accessing beans via the ApplicationContext");
      }
      return this.beanFactory;
   }
}

这里其实就是把 refreshBeanFactory 方法创建并初始化的 BeanFactory 获取到并返回。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/31008.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业快速构建可落地的IT服务管理体系的五大关键点

随着数字化转型的发展,IT运维管理环境日益复杂,对管理的要求也随之增高如何提升运维效率,快速落地做好运维管理,搭建一套IT服务管理必不可少,以往我们也对IT服务管理框架进行过总结,当下,面对很…

Linux ALSA 之三:简单的 ALSA Driver 实现

简单的 ALSA Driver 实现一、概述二、Linux ALSA 音频设备驱动实例1、注册 Platform Device & Platform Driver2、创建 card3、PCM 设备相关设定3.1 创建 PCM Device3.2 设置 PCM 操作3.2 PCM HW 初始化4、Control 设备相关设定4.1 定义 snd_kcontrol_new4.2 构造 control5…

C语言实现冒泡排序(图解)

目录 一、冒泡排序是什么? 二、图解冒泡排序过程 三、代码实现 3.1易错点(切记切记) 四、优化 4.1优化代码 一、冒泡排序是什么? int arr[]{9,8,7,6,5,4,3,2,1,0} ,像这样的数组,升序排序。 冒泡排序…

Dynamic Potential-Based Reward Shaping将势能塑形奖励函数拓展为F(s,t,s‘,t‘)

摘要 基于势能的奖励塑形可以显著降低学习最优策略所需的时间,并且在多agent系统中,可以显著提高最终联合策略的性能。已经证明,它不会改变一个agent单独学习的最优策略或多个agent一起学习的纳什均衡。 ------然而,现有证明的一…

正厚软件-软件测试用例设计方法之二-边界值

正厚软件-刘老师的干货分享 上一课我们学习测试用例的等价类划分法,今天我们看下边界值法。 一、方法简介 1、定义:边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充。 2、边界值与等价类…

Linux零拷贝原理学习

文章目录一、为什么要有 DMA 技术?二、传统的文件传输有多糟糕?三、如何优化文件传输的性能?四、 如何实现零拷贝?mmap writesendfile使用零拷贝技术的项目五、PageCache 有什么作用?六、大文件传输用什么方式实现?一…

c++动态创建二维数组和释放

动态创建二维数组和释放 文章目录创建参考博客😊点此到文末惊喜↩︎ 创建 指针数组的方式 使用malloc和free可以兼容c相比于使用STL可以更加灵活但是debug可能数组显示不全 // 初始化int **arr;int row 5;//用于表示行数int col 5;//用于表示列数arr new int…

网站升级HTTPS教程

现在越来越多的网站开始使用https协议,其实百度从2014年底就已经开始支持https了,并且据说在排名上,同权重的网站,开启https会优待提升排名。先不管排不排名吧,https是一种更安全更先进的技术。作为互联网的弄潮儿&…

MPU9250数据转换

MPU9250数据转换1. MPU92501.1 MPU9250介绍2. 数据转换2.1 陀螺仪数据转换2.2 加速度数据转换1. MPU9250 1.1 MPU9250介绍 1)MPU9250 内部集成有 3 轴陀螺仪、3 轴加速度计和 3 轴磁力计 2)输出: 16 位的数字量; 3) 通过集成电路…

MCE | 免疫检查点大组团

提到免疫,总会让人联想到由免疫失调引起的疾病,例如病毒感染,自身免疫病类风湿性关节炎,癌症等。实际上,机体的免疫应答受到严格的调控,并存在多种机制预防对自身蛋白的免疫反应。在过去的 20 年中&#xf…

3.2、封装成帧

3.2、封装成帧 3.2.1、封装成帧 数据链路层给上层交付的协议数据单元添加帧头和帧尾使之成为帧 在帧头和帧尾中包含有重要的控制信息 数据链路层将上层交付下来的协议数据单元封装成帧后,通过物理层将构成帧的各比特转换成电信号发送到传输媒体 那么接收方的数据…

dreamweaver作业静态HTML网页设计——摩尔庄园7页HTML+CSS+JS DW大学生网页作业制作设计 Dreamweaver简单网页

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置,有div的样式格局,这个实例比较全面,有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

15_移动端项目或者前后端分离项目接口规范

15_移动端项目或者前后端分离项目接口规范写在前面的话,主要是谈谈接口 随着前后端的分离,后端工程师不需要编写页面,甚至不需要编写JavaScript代码,只需要提供接口给前端工程师即可,可是就是仅仅一个接口&#xff0c…

mybatis-plus自带的乐观锁

文章目录1.场景1.1.模拟冲突2.添加乐观锁2.1数据库添加字段2.2配置文件中增加乐观锁拦截器2.3类的属性上添加注解2.4再次运行测试文件3.优化流程mysbatis-plus乐观锁原理:mysbatis-plus进行修改操作时,会将数据库中version字段的值拿出来和上一个查询时的…

大一学生WEB前端静态网页——旅游网页设计与实现-张家口 6页

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材,DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 游景点介绍 | 旅游风景区 | 家乡介绍 | 等网站的设计与制作| HTML期末大学生网页设计作业 HTML:结构 CSS:样式 在操作方面上运…

【电力负荷预测】模拟退火算法结合狮群算法优化Elman神经网络电力负荷预测【含Matlab源码 1454期】

⛄一、模拟退火算法简介 1 引言 模拟退火算法(Simulated Annealing,SA)的思想最早由Metropolis等人于1953年提出:Kirkpatrick于1983年第一次使用模拟退火算法求解组合最优化问题[1] 。模拟退火算法是一种基于MonteCarlo迭代求解策略的随机寻优算法&…

图文详解Linux基础经典教程(09)——部署项目至CentOS

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 概述 在之前的操作里,我们在CentOS中安装了JDK、Tomcat、MySQL。接下来,我们需要将JavaWeb项目部署至CentOS。 项目开发 请在IDEA中开发JavaWeb项…

SpringBoot SpringBoot 开发实用篇 6 监控 6.3 actuator

SpringBoot 【黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战(spring boot2完整版)】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇6 监控6.3 actuator6.3.1 actuator6.3.2 监控原理6.3.3 小结6 监控 …

Win10下安装CARLA

在仿真环境中要使用lidar进行测试,目前prescan和matlab的lidar扫描方式无法设置,而CARLA中lidar是机械扫描形式,符合需求故选择该软件,只是测试不想重装系统,便在win10下进行安装测试。 1. 安装前需要安装的软件 1. …

小白必看 最核心的5大TikTok视频营销策略(附赠工具)

数据显示,TikTok的用户月人均使用时长达到了每月23.6小时,超过了YouTube的23.2小时。TikTok的用户支出在2022年第一季度达到了8.4亿美元,迄今为止其用户支出总额已超过46亿美元。可见,Tiktok是目前发展相对较好的的短视频社交媒体…