Spring之实例化Bean(2)

news2024/11/15 11:20:21

Spring是非常复杂的一个框架,想要一篇博客就说完实例化Bean的全流程,那将注定会是是很长很长的篇博客,说实话,换做我自己是没有耐心看完的。

本章,我将会以一个最简单的例子来阐述Spring实例化Bean的过程,下一章我们将会重点基于本章节的实例化过程,来阐释一些复杂的实例化过程。 比如:@Autowired @Resource如何注册的,以及循环依赖的大流程。

在开始今天的源码分析之前,我们需要强调几个概念,不懂没关系,先记住就好:

首先,getBean(beanName)方法就是实例化bean的方法,而且是核心方法。

其次, spring中会存在1级、2级、3级缓存的概念。1级缓存是存放已经实例化好的bean的,2级缓存和3级缓存都是存放正在实例化过程中的bean的相关信息的。

接着上一篇Spring之基于注解方式实例化BeanDefinition(1)_chen_yao_kerr的博客-CSDN博客,我们知道,在spring开始实例化之前,我们都是需要首先把java类封装成BeanDefinition对象的。

今天,我们看一下本章测试的最简单的Demo

package com.xiangxue.jack.bean;

import org.springframework.stereotype.Repository;

import javax.annotation.PostConstruct;

@Repository
public class Dao {

    private String name;

    private String id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @PostConstruct  //相当于init-method
    void init () {
        id = "001";
        name = "yy";
    }
    @Override
    public String toString() {
        return "name :" + name + " id :" + id;
    }
}

今天,我就从这个最简单的类实例化开始将起。

 1. 首先进入AbstractApplicationContext的refresh方法中,我们直接跳转到实例化bean的方法处

 进入这个方法 :

 继续进入:

此时,我们见到了熟悉的面孔了。beanDefitionNames这个集合是放所有的beanDenation的name值的,然后我们根据name获取到BeanDefinition对象。 此时,我们知道,只有是单例、非抽象、非懒加载的BeanDefinition才有资格继续进行实例化操作

2. 此时,代码继续往下走,我们发现它调用了getBean方法,而本章节一开始我就说过,getBean方法就是实例化Bean的。因此,此时应该是进入了实例化bean的主流程了,逐层debug进去:

3. getBean(beanName)方法内部调用了doGetBean方法进行实例化的,继续跟进去,我们发现它是从缓存中拿的。而第一次实例化之前,肯定是拿不到任何bean的对象的。

此处,我们有必要点进去看一下getSingleton方法,看一下3级缓存。其实,就是3个集合而已。此处稍微记一下就好,本章节不会就3级缓存展开解释。

4. 此时,我们接着 doGetBean方法继续主干流程讲解。中间会有父子类合并,@DependOn的判断等等,全部跳过。我们直接来到实例化bean的核心代码处

 此时,我们需要关注2个方法,分别是getSingleton方法和createBean方法。这是一个lambda表达式, 流程就是先调用getSingleton方法,然后回调createBean方法

getSingleton:首先从1级缓存中拿,拿不到就把当前的beanDefinition的名字放入集合  singletonsCurrentlyInCreation中,表示当前beanDefinition正在实话bean;

 然后调用singletonObject = singletonFactory.getObject(); 进入到createBean方法

 

createBean : 在createBea方法中,我们会调用到doCreateBean方法, 然后在doCreateBean内部会调用到createBeanInstance方法

 

此时,我们进入到createBeanInstance方法中:其实,在这个方法中,我们知道实例化的2种方法,一个是有参构造函数,另一个是无参构造函数。而本章节的demo,我们是最简单的实例化步骤,因此是无参构造函数

 

 到此时,我们知道最终返回值是一个经过包装的BeanWrapper对象。但是,我们继续debug进去看实例化的具体步骤:

继续跟进,我们知道就是反射调用生成对象了

反射实例化完成以后,我们将对象包装成BeanWrapper对象进行返回。

5. 此时,我们还是回到了doCreateBean方法体中,继续往下走。

我们会遇到方法applyMergedBeanDefinitionPostProcessors,此处我们稍微介绍一下。这个方法其实就是搜集注解的作用。比如:@Autowired @Resource @PostConstruct等等。

这些收集注解的工作,是我们在实例化BeanDefinition的时候,通过调用registerComponents方法注进去的各种PostProcessor实现的。上一章的补充说明部分。可以直接在Spring之基于注解方式实例化BeanDefinition(1)_chen_yao_kerr的博客-CSDN博客

中搜索 “最后补充一下”。

继续回到applyMergedBeanDefinitionPostProcessors方法:

 此处,我仅仅列出了一个CommonAnnotationBeanPostProcessor作为举例。

6. 言归正传,我们还是继续doCreateBean方法, 往下走:接下来我们会遇到3个比较重要的方法,逐个列举出来解释:

addSingletonFactory : 添加缓存

populateBean: ioc di,依赖注入的核心方法。 本章是最简单的实例化过程,不涉及。下章节会重点分析。

initializeBean:实例化+ioc依赖注入完以后的调用,简单概括就是对象实例化完成以后,里面的变量,无论是通过注解注入的,还是调用什么方法初始化的,此时都没有值。本章中,我们使用到了@PostConstruct注解,那么我们此处简单的分析一下这个方法。下章节再重点分析。

进入这个方法,我们直接来到核心代码处: 

 点进去,我们发现,它又是调用我们在实例化BeanDefinition是注册的那些PostProcessor。我们使用到了@PostConstruct注解,那么我们还是以CommonAnnotationBeanPostProcessor为例进行分析:

 进入CommonAnnotationBeanPostProcessor的postProcessBeforeInitialization方法:

 这里,我们先来解释一下MetaData类。每个java都会生成一个BeanDefinition和一个MetaData对象,而MetaData里面存储了类的基本信息,比如:这个类的父类是谁,使用了哪些注解,有哪些方法,类和方法的作用域等各种各样的信息。MetaData会被大量的使用,以后我们会经常看到。

继续跟进这个方法:

此时,在我们的Dao.java文件的init方法上打上断点,代码会直接进入我们的方法进行初始化操作。因为它使用了@PostConstruct注解:

   @PostConstruct  //相当于init-method
    void init () {
        id = "001";
        name = "yy";
    }

返回到 applyBeanPostProcessorsBeforeInitialization方法中,我们可以确认dao对象已经被初始化完成:

继续返回上一层方法 initializeBean 中。此时,下面还有2个比较重要的方法,本例暂时没有用到,暂时忽略。

继续返回上一层方法中,此时由来到了doCreateBean方法中了。此时,它会注册bean销毁时的类DisposableBeanAdapter,最后返回bean对象。

继续返回上一层方法中,此时来到了createBean方法中。

等等,前面好像说过createBean方法,从第4步开始介绍createBean,一直到现在才最终回到了createBean方法中,而且这个方法内部又调用了N多实例化的核心方法,值得反复研究。此时,createBean方法来到了结尾处了:

返回createBean方法的上一层:

 我们之前说过,在调用getSingleton方法的时候会回调进入createBean方法,那此时createBean方法结束了,接下来又会进入到getSingleton方法中了

继续debug方法 getSingleton 往下走:此时会完成2件事

第4步一开始的时候,我们说从一级缓存中拿不到beanName的对象,会把beanName放入一个集合中,表示这个bean正在实例化。而此时,我们实例化和初始化都完成了,需要把当前的beanName从表示正在创建的集合中删除掉。

第4步一开始的时候就说从缓存中拿beanName的对象,那时候拿不到。现在已经完成实例化了。需要将当前实例化的bean放入1级缓存,并从2、3级缓存中删除。

 

 哇塞,说了这么多,终于完成了第4步全部流程的梳理。就这么一小段代码,反反复复的调用,干了如此多的事情,真是不容易啊

	if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					//改方法是FactoryBean接口的调用入口
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

7. 好了,我们又回到了主流程方法 doGetBean 中了。接下来就是一路返回。至此,实例化Bean对象也已经全部完成了。

测试:

下面开始我们的测试

package com.xiangxue.jack;

import com.xiangxue.jack.bean.Dao;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;

//@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring.xml"})
public class MyTest {

    @Autowired
    private ApplicationContext applicationContext;

   
    @Test
    public void myBean3() {
        applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Dao dd = (Dao) applicationContext.getBean("dao");
        System.out.println(dd.toString());
    }
}

直接debug 进入getBean方法中,只要我们从1级缓存中直接拿到对象就说明整个流程梳理都是对的。

 确实是拿到了实例化并且初始化后的bean对象了,而且控制台也正确的打印了出来。测试成功

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

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

相关文章

8.SpringSecurity中的核心过滤器-CsrfFilter

SpringSecurity中的核心过滤器-CsrfFilter Spring Security除了认证授权外功能外,还提供了安全防护功能。本文我们来介绍下SpringSecurity中是如何阻止CSRF攻击的。 一、什么是CSRF攻击 跨站请求伪造(英语:Cross-site request forgery&#…

uos 20 统信 fprintd 记录

uos 20 统信 fprintd 记录 sudo busctl deepin-authenticate.service /usr/lib/systemd/system/deepin-authenticate.service [Unit] DescriptionDeepin Authentication[Service] Typedbus BusNamecom.deepin.daemon.Authenticate ExecStart/usr/lib/deepin-authenticate/d…

Activty启动到显示的过程[二]

Activity的显示从handleResumeActivity()方法开始。 //ActivityThread.javaOverridepublic void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {final ActivityClientRecord r performResumeActivity(token, finalStat…

LCD液晶段码驱动IC/LCD液晶驱动芯片VK2C22高抗干扰/抗噪,适用于汽车仪表/单相智能电表

产品型号:VK2C22A/B产品品牌:永嘉微电/VINKA封装形式:LQFP52/48、DICE(COB邦定片)、COG(邦定玻璃用)产品年份:新年份原厂,工程服务,技术支持!VK2C22A/B概述:VK2C22是一个点阵式存储映…

自然语言处理-基于预训练模型的方法-chapter3基础工具集与常用数据集

文章目录3.1NLTK工具集3.1.1常用语料库和词典资源3.1.2常见自然语言处理工具集3.2LTP工具集3.3pytorch基础3.3.1张量基本概念3.3.2张量基本运算3.3.3自动微分3.3.4调整张量形状3.3.5广播机制3.3.6索引与切片3.3.7降维与升维3.4大规模预训练模型3.1NLTK工具集 3.1.1常用语料库和…

2023年3月软考高项(信息系统项目管理师)报名走起!!!

信息系统项目管理师是全国计算机技术与软件专业技术资格(水平)考试(简称软考)项目之一,是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试,既属于国家职业资格考试,又是职称资…

图表控件LightningChart.NET 系列教程(十一):LightningChart 组件——添加至 Blend WPF 项目

LightningChart.NET 是一款高性能 WPF 和 Winforms 图表,可以实时可视化多达1万亿个数据点。可有效利用CPU和内存资源,实时监控数据流。同时,LightningChart使用突破性创新技术,以实时优化为前提,大大提升了实时渲染的效率和效果&…

网络编程 socket 编程(一)

1. C/S 架构 C/S 架构即客户端/服务端架构,B/S 架构(浏览器与服务端)也是 C/S 架构的一种。 C/S 架构与 socket 的关系:学习 socket 可以完成 C/S 架构的开发。 2. osi 七层 一个完整的计算机系统由硬件、操作系统以及应用软件…

Redis:主从同步

Redis:主从同步一. 概述二. 原理(1) 全量同步(2) 增量同步(3) 优化Redis主从集群三. 总结一. 概述 引入: Redis主从集群采用一个Master负责写,多个Slave负责读的方式(读多写少),那么如何让读取数据时多个从…

访问学者进入美国哪些东西不能带?

随着疫情的稳定,各国签证的逐步放开,成功申请到国外访问学者、博士后如何顺利的进入国外,哪些东西不能带,下面就随知识人网小编一起看一看。一、畜禽肉类(Meats, Livestock and Poultry)不论是新鲜的、干燥的、罐头的、真空包装的…

pycharm激活虚拟环境时报错:无法加载文件activate.ps1,因为在此系统上禁止运行脚本,Windows10系统

问题: ii_env\Scripts\activate : 无法加载文件 F:\gitlab\AutoFrame\ii_env\Scripts\Activate.ps1,因为在此系统上禁止运行脚本。 有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID135170 中的 about_Execution_Policies。 所在…

34 openEuler使用LVM管理硬盘-创建并挂载文件系统

文章目录34 openEuler使用LVM管理硬盘-创建并挂载文件系统34.1 创建文件系统34.2 手动挂载文件系统34.3 自动挂载文件系统34 openEuler使用LVM管理硬盘-创建并挂载文件系统 在创建完逻辑卷之后,需要在逻辑卷之上创建文件系统并挂载文件系统到相应目录下。 34.1 创…

大型医院云HIS系统:采用前后端分离架构,前端由Angular语言、JavaScript开发;后端使用Java语言开发 融合B/S版电子病历系统

一套医院云his系统源码 采用前后端分离架构,前端由Angular语言、JavaScript开发;后端使用Java语言开发。融合B/S版电子病历系统,支持电子病历四级,HIS与电子病历系统均拥有自主知识产权。 文末卡片获取联系! 基于云计…

谷歌留痕霸屏平台有哪些?

谷歌留痕霸屏平台有哪些? 答案是:光算可以做谷歌留痕霸屏 我们要先了解什么是谷歌留痕霸屏平台这个概念。 很简单,就是你有哪些可以做排名的网站资源,一般情况下你不够专业,是没办法把这件事做好的。 通常你要做谷…

学python的第四天---基础(2)

一、三角形类型读入数组并排序的方法nlist(map(float,input().split())) c,b,asorted(n)list_1 list(map(float, input().split())) list_1.sort() list_1.reverse()lengthssorted(map(float,input().split(" ")),reverseTrue)二、动物写法一:d{" &…

css系统化学习

元素的语义化 SEO:搜索引擎优化 根据搜索引擎展示的规律,语义化的元素更容易被展示获得更多浏览量 字符编码 css历史 内联样式(inline) style"内容全写在等号后面,双引号里面,多个之间用;隔开" 内部样式(internal) style写在head里面,在title下面,不是在body内, …

Hadoop集群搭建,基于3.3.4hadoop和centos8【图文教程-从零开始搭建Hadoop集群】,常见问题解决

Hadoop集群搭建,基于3.3.4hadoop和centos8【小白图文教程-从零开始搭建Hadoop集群】,常见问题解决Hadoop集群搭建,基于3.3.4hadoop1.虚拟机的创建1.1 第一台虚拟机的创建1.2 第一台虚拟机的安装1.3 第一台虚拟机的网络配置1.3.1 主机名和IP映…

10.单点登录原理及JWT实现

单点登录原理及JWT实现 一、单点登录效果 首先我们看通过一个具体的案例来加深对单点登录的理解。案例地址:https://gitee.com/xuxueli0323/xxl-sso?_fromgitee_search 把案例代码直接导入到IDEA中 然后分别修改下server和samples中的配置信息 在host文件中配置 …

Python实现多键字典

实现背景 在许多场景中,有时需要通过多种信息来获取某个特定的值,而各种编程语言(包括Python)使用的字典(Dict)数据结构通常只支持单个键值寻值key-val对,即“一对一”(一个键对应一…

哈工大服务科学与工程第一章作业

服务的概念服务是个非常广义的概念——涉及到经济、管理、业务、IT领域以下是一些各方对服务的定义:服务是一方向另一方提供的任意活动和好处。它是不可触知的,不形成任何所有权问题,其生产可能与物质产品有关,也可能无关。服务是…