Spring之实例化Bean(2)
创始人
2024-05-29 15:24:43
0

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-methodvoid init () {id = "001";name = "yy";}@Overridepublic 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-methodvoid 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 {@Autowiredprivate ApplicationContext applicationContext;@Testpublic void myBean3() {applicationContext = new ClassPathXmlApplicationContext("spring.xml");Dao dd = (Dao) applicationContext.getBean("dao");System.out.println(dd.toString());}
}

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

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

相关内容

热门资讯

锤子如何升级安卓系统,轻松迈向... 亲爱的手机控们,你是不是也和我一样,对锤子手机的那颗“坚果”心念念不忘呢?不过,你知道吗?为了让你的...
安卓系统和linux系统互通吗... 你有没有想过,安卓系统和Linux系统之间是不是能来个亲密接触呢?想象你的安卓手机和Linux电脑无...
安卓系统升级作用,提升系统性能... 你知道吗?每次你的安卓手机更新系统,就像是给它来了一次全面的“SPA”护理,不仅让它焕然一新,还能让...
安卓系统手写连写设置,个性化设... 你有没有发现,用安卓手机写东西的时候,手写输入有时候超级方便呢?尤其是当你想要快速记录一些灵感或者笔...
小米手机的安卓系统,深度解析安... 你有没有发现,最近小米手机的安卓系统好像变得特别不一样了?没错,今天咱们就来聊聊这个话题,看看小米手...
电视安卓系统文件快传,快传功能... 亲爱的读者们,你是否曾在使用电视时,因为传输文件而感到烦恼?别担心,今天我要给你带来一个超级实用的技...
安卓系统下载盒精灵,安卓系统下... 你有没有想过,家里的电视也能变得像智能手机一样智能呢?没错,这就是安卓系统下载盒精灵的魅力所在。今天...
安卓系统编写应用,打造个性化移... 你有没有想过,手机里那些让你爱不释手的APP,其实都是用安卓系统编写出来的呢?没错,就是那个让我们的...
索纳塔安卓系统软件,智能驾驶体... 你有没有发现,现在的汽车越来越智能了?这不,最近我试驾了一辆搭载了索纳塔安卓系统软件的新车,简直让我...
车载安卓系统怎么重装,轻松恢复... 你那车载安卓系统是不是突然间就卡得跟蜗牛一样慢了?别急,今天就来手把手教你如何重装车载安卓系统,让你...
小米手机点击安卓系统,安卓系统... 你有没有发现,最近小米手机在市场上可是越来越火了?尤其是它搭载的安卓系统,简直让人爱不释手。今天,就...
安卓系统哪些公司开发,安卓系统... 你有没有想过,我们每天离不开的安卓系统,究竟是由哪些公司联手打造的?这背后可是有着不少有趣的故事呢!...
安卓系统显示网络错误,安卓系统... 最近是不是发现你的安卓手机屏幕上时不时地跳出“网络错误”的提示?别急,这可不是什么世界末日,只是小插...
索尼的原生安卓系统,极致流畅体... 你有没有想过,为什么索尼的智能手机总是那么特别?没错,就是那个拥有原生安卓系统的索尼!今天,就让我带...
安卓系统可以打印吗,开启移动打... 你有没有想过,你的安卓手机或者平板,除了打电话、刷微博、玩游戏,还能干点啥?没错,今天我要给你揭秘一...
安卓怎么调用系统拨号,Andr... 你是不是也和我一样,对安卓手机的系统功能充满了好奇?比如说,你知道怎么让你的安卓手机像打电话一样,直...
什么叫安卓双系统,一机多能的智... 你有没有想过,为什么你的手机可以同时运行两个操作系统呢?这就是我们今天要聊一聊的安卓双系统。想象一边...
安卓系统有哪些表情,轻松表达你... 你知道吗?安卓系统里的表情包那可真是五花八门,让人眼花缭乱。今天,就让我带你一起探索一下安卓系统里那...
安卓系统怎么下载树洞,安卓系统... 你有没有想过,在安卓手机上下载一个可以让你畅所欲言、分享心情的树洞应用呢?树洞,顾名思义,就是一个可...
山寨安卓导航升级系统,功能升级... 你有没有发现,手机导航越来越智能了?但是你知道吗,市面上那些看似高大上的安卓导航升级系统,其实有不少...