0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Spring中Bean的生命周期是怎样的?

jf_ro2CN3Fa 来源:楼仔 作者:楼仔 2022-10-11 15:08 次阅读

1. 基础知识

1.1 什么是 IoC ?

1.2 Bean 生命周期

1.3 执行流程

1.4 扩展方法

2. 源码解读

2.1 代码入口

2.2 实例化

2.3 属性赋值

2.4 初始化

2.5 销毁

3. 写在最后

Spring Bean 的生命周期,面试时非常容易问,这不,前段时间就有个读者去面试,因为不会回答这个问题,一面都没有过。

如果只讲基础知识,感觉和网上大多数文章没有区别,但是我又想写得稍微深入一点。

考虑很多同学不喜欢看源码,我就把文章分为 2 大部分,前面是基础知识,主要方便大家面试和学习 ,后面是源码部分,对源码感兴趣的同学可以继续往后面看。

不 BB,上文章目录。

f4755436-4162-11ed-96c9-dac502259ad0.png

1. 基础知识

1.1 什么是 IoC ?

IoC,控制反转,想必大家都知道,所谓的控制反转,就是把 new 对象的权利交给容器,所有的对象都被容器控制,这就叫所谓的控制反转。

IoC 很好地体现了面向对象设计法则之一 —— 好莱坞法则:“别找我们,我们找你 ”,即由 IoC 容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

理解好 IoC 的关键是要明确 “谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”。

f4936020-4162-11ed-96c9-dac502259ad0.png

谁控制谁,控制什么?

传统 Java SE 程序设计,我们直接在对象内部通过 new 进行创建对象,是程序主动去创建依赖对象。而 IoC 是由专门一个容器来创建这些对象,即由 IoC 容器来控制对象的创建。

谁控制谁?当然是 IoC 容器控制了对象;

控制什么?主要控制了外部资源获取(不只是对象,比如包括文件等)。

为何是反转,哪些方面反转了?

有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转,而反转则是由容器来帮忙创建及注入依赖对象。

为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;

哪些方面反转了?依赖对象的获取被反转了。

1.2 Bean 生命周期

对 Prototype Bean 来说,当用户 getBean 获得 Prototype Bean 的实例后,IOC 容器就不再对当前实例进行管理,而是把管理权交由用户,此后再 getBean 生成的是新的实例。

所以我们描述 Bean 的生命周期,都是指的 Singleton Bean。

f51a6804-4162-11ed-96c9-dac502259ad0.png

Bean 生命周期过程:

实例化 :第 1 步,实例化一个 Bean 对象;

属性赋值 :第 2 步,为 Bean 设置相关属性和依赖;

初始化 :初始化的阶段的步骤比较多,5、6 步是真正的初始化,第 3、4 步为在初始化前执行,第 7 步在初始化后执行,初始化完成之后,Bean 就可以被使用了;

销毁 :第 8~10 步,第 8 步其实也可以算到销毁阶段,但不是真正意义上的销毁,而是先在使用前注册了销毁的相关调用接口,为了后面第 9、10 步真正销毁 Bean 时再执行相应的方法。

整个执行流程稍微有些抽象,下面我们通过代码,来演示执行流程。

1.3 执行流程

创建一个 LouzaiBean。

publicclassLouzaiBeanimplementsInitializingBean,BeanFactoryAware,BeanNameAware,DisposableBean{

/**
*姓名
*/
privateStringname;

publicLouzaiBean(){
System.out.println("1.调用构造方法:我出生了!");
}

publicStringgetName(){
returnname;
}

publicvoidsetName(Stringname){
this.name=name;
System.out.println("2.设置属性:我的名字叫"+name);
}

@Override
publicvoidsetBeanName(Strings){
System.out.println("3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名");
}

@Override
publicvoidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException{
System.out.println("4.调用BeanFactoryAware#setBeanFactory方法:选好学校了");
}

@Override
publicvoidafterPropertiesSet()throwsException{
System.out.println("6.InitializingBean#afterPropertiesSet方法:入学登记");
}

publicvoidinit(){
System.out.println("7.自定义init方法:努力上学ing");
}

@Override
publicvoiddestroy()throwsException{
System.out.println("9.DisposableBean#destroy方法:平淡的一生落幕了");
}

publicvoiddestroyMethod(){
System.out.println("10.自定义destroy方法:睡了,别想叫醒我");
}

publicvoidwork(){
System.out.println("Bean使用中:工作,只有对社会没有用的人才放假。。");
}
}

自定义一个后处理器 MyBeanPostProcessor。

publicclassMyBeanPostProcessorimplementsBeanPostProcessor{

@Override
publicObjectpostProcessBeforeInitialization(Objectbean,StringbeanName)throwsBeansException{
System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦");
returnbean;
}

@Override
publicObjectpostProcessAfterInitialization(Objectbean,StringbeanName)throwsBeansException{
System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!");
returnbean;
}
}

applicationContext.xml 配置文件(部分)。





测试入口:

publicclassMyTest{
publicstaticvoidmain(String[]args){
ApplicationContextcontext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");
LouzaiBeanlouzaiBean=(LouzaiBean)context.getBean("louzaiBean");
louzaiBean.work();
((ClassPathXmlApplicationContext)context).destroy();
}
}

执行结果:

1.调用构造方法:我出生了!
2.设置属性:我的名字叫楼仔
3.调用BeanNameAware#setBeanName方法:我要上学了,起了个学名
4.调用BeanFactoryAware#setBeanFactory方法:选好学校了
5.BeanPostProcessor.postProcessBeforeInitialization方法:到学校报名啦
6.InitializingBean#afterPropertiesSet方法:入学登记
7.自定义init方法:努力上学ing
8.BeanPostProcessor#postProcessAfterInitialization方法:终于毕业,拿到毕业证啦!
Bean使用中:工作,只有对社会没有用的人才放假。。
9.DisposableBean#destroy方法:平淡的一生落幕了
10.自定义destroy方法:睡了,别想叫醒我

这个流程非常清晰,Bean 生命周期流程图能完全对应起来。

1.4 扩展方法

我们发现,整个生命周期有很多扩展过程,大致可以分为 4 类:

Aware 接口:让 Bean 能拿到容器的一些资源,例如 BeanNameAware 的 setBeanName() ,BeanFactoryAware 的 setBeanFactory()

后处理器:进行一些前置和后置的处理,例如 BeanPostProcessor 的 postProcessBeforeInitialization()postProcessAfterInitialization()

生命周期接口:定义初始化方法和销毁方法的,例如 InitializingBean 的 afterPropertiesSet() ,以及 DisposableBean 的 destroy()

配置生命周期方法:可以通过配置文件,自定义初始化和销毁方法,例如配置文件配置的 init()destroyMethod()

2. 源码解读

注意:Spring 的版本是 5.2.15.RELEASE ,否则和我的代码不一样!!!

上面的知识,网上其实都有,下面才是我们的重头戏,让你跟着我走一遍代码流程。

2.1 代码入口

f52ef8d2-4162-11ed-96c9-dac502259ad0.pngf5592b2a-4162-11ed-96c9-dac502259ad0.png

这里需要多跑几次,把前面的 beanName 跳过去,只看 louzaiBean。

f58a2bc6-4162-11ed-96c9-dac502259ad0.pngf5c39cc6-4162-11ed-96c9-dac502259ad0.png

进入 doGetBean(),从 getSingleton() 没有找到对象,进入创建 Bean 的逻辑。

f5ded540-4162-11ed-96c9-dac502259ad0.pngf614cd62-4162-11ed-96c9-dac502259ad0.png

2.2 实例化

进入 doCreateBean() 后,调用 createBeanInstance()。

f628265a-4162-11ed-96c9-dac502259ad0.png

进入 createBeanInstance() 后,调用 instantiateBean()。

f63fbda6-4162-11ed-96c9-dac502259ad0.pngf65af058-4162-11ed-96c9-dac502259ad0.pngf6687e58-4162-11ed-96c9-dac502259ad0.pngf68253fa-4162-11ed-96c9-dac502259ad0.pngf6b7d0de-4162-11ed-96c9-dac502259ad0.png

走进示例 LouzaiBean 的方法,实例化 LouzaiBean。

f6ca7ec8-4162-11ed-96c9-dac502259ad0.png图片

2.3 属性赋值

再回到 doCreateBean(),继续往后走,进入 populateBean()。

这个方法非常重要,里面其实就是依赖注入的逻辑,不过这个不是我们今天的重点,大家如果对依赖注入和循环依赖感兴趣,可以翻阅我之前的文章。

f6e63e56-4162-11ed-96c9-dac502259ad0.png

进入 populateBean() 后,执行 applyPropertyValues()

f701bd5c-4162-11ed-96c9-dac502259ad0.png

进入 applyPropertyValues(),执行 bw.setPropertyValues()

f70dedfc-4162-11ed-96c9-dac502259ad0.pngf737e12a-4162-11ed-96c9-dac502259ad0.pngf75d67a6-4162-11ed-96c9-dac502259ad0.pngf76e0f84-4162-11ed-96c9-dac502259ad0.png

进入 processLocalProperty(),执行 ph.setValue()。

f787e59e-4162-11ed-96c9-dac502259ad0.pngf7b03ee0-4162-11ed-96c9-dac502259ad0.pngf7cf9e52-4162-11ed-96c9-dac502259ad0.png

走进示例 LouzaiBean 的方法,给 LouzaiBean 赋值 name。

f7fd9c6c-4162-11ed-96c9-dac502259ad0.png

到这里,populateBean() 就执行完毕,下面开始初始化 Bean。

2.4 初始化

我们继续回到 doCreateBean(),往后执行 initializeBean()。

f82a1116-4162-11ed-96c9-dac502259ad0.pngf847984e-4162-11ed-96c9-dac502259ad0.pngf8594698-4162-11ed-96c9-dac502259ad0.png

走进示例 LouzaiBean 的方法,给 LouzaiBean 设置 BeanName。

f873cdba-4162-11ed-96c9-dac502259ad0.png

回到 invokeAwareMethods()。

f8814026-4162-11ed-96c9-dac502259ad0.png

走进示例 LouzaiBean 的方法,给 LouzaiBean 设置 BeanFactory。

f8e5b308-4162-11ed-96c9-dac502259ad0.png

第一次回到 initializeBean() ,执行下面逻辑。

f8f35ff8-4162-11ed-96c9-dac502259ad0.png

这里需要多循环几次,找到 MyBeanPostProcessor 的策略方法。

f93dad38-4162-11ed-96c9-dac502259ad0.png

我们自己定义的后置处理方法。

f988cc32-4162-11ed-96c9-dac502259ad0.png

第二次回到 initializeBean() ,执行下面逻辑。

f9b3dec2-4162-11ed-96c9-dac502259ad0.pngf9d8cf98-4162-11ed-96c9-dac502259ad0.png

走进示例 LouzaiBean 的方法,执行 afterPropertiesSet()。

f9f65ab8-4162-11ed-96c9-dac502259ad0.png

返回 invokeInitMethods(),执行下面逻辑。

fa1bbf24-4162-11ed-96c9-dac502259ad0.png

进入 invokeCustomInitMethod(),执行下面逻辑。

fa45b18a-4162-11ed-96c9-dac502259ad0.png

走进示例 LouzaiBean 的方法,执行 init()。

fa716384-4162-11ed-96c9-dac502259ad0.png

第三次回到 initializeBean() ,执行下面逻辑。

fa8e46ca-4162-11ed-96c9-dac502259ad0.pngfaa8e7a0-4162-11ed-96c9-dac502259ad0.png

我们自己定义的后置处理方法。

fb2152da-4162-11ed-96c9-dac502259ad0.png

到这里,初始化的流程全部结束,都是围绕 initializeBean() 展开。

2.5 销毁

当 louzaiBean 生成后,后面开始执行销毁操作,整个流程就比较简单。

fb47debe-4162-11ed-96c9-dac502259ad0.pngfb5f720e-4162-11ed-96c9-dac502259ad0.pngfb80dbba-4162-11ed-96c9-dac502259ad0.pngfba80762-4162-11ed-96c9-dac502259ad0.pngfbcf11b8-4162-11ed-96c9-dac502259ad0.pngfbf49f1e-4162-11ed-96c9-dac502259ad0.pngfc20e506-4162-11ed-96c9-dac502259ad0.pngfc3fcb38-4162-11ed-96c9-dac502259ad0.pngfc6ac6b2-4162-11ed-96c9-dac502259ad0.pngfc820aac-4162-11ed-96c9-dac502259ad0.png

走进示例 LouzaiBean 的方法,执行 destroy()。

fca82fb6-4162-11ed-96c9-dac502259ad0.png

回到 destroy(),执行下面逻辑。

fcc53da4-4162-11ed-96c9-dac502259ad0.pngfcfc5582-4162-11ed-96c9-dac502259ad0.pngfd9e5bac-4162-11ed-96c9-dac502259ad0.png

走进示例 LouzaiBean 的方法,执行 destroyMethod()。

fdd07268-4162-11ed-96c9-dac502259ad0.png

到这里,所有的流程全部结束,文章详细描述所有的代码逻辑流转,你可以完全根据上面的逻辑,自己 debug 一遍。

3. 写在最后

我们再回顾一下几个重要的方法:

doCreateBean() :这个是入口;

createBeanInstance() :用来初始化 Bean,里面会调用对象的构造方法;

populateBean() :属性对象的依赖注入,以及成员变量初始化;

initializeBean() :里面有 4 个方法,

先执行 aware 的 BeanNameAware、BeanFactoryAware 接口;

再执行 BeanPostProcessor 前置接口;

然后执行 InitializingBean 接口,以及配置的 init();

最后执行 BeanPostProcessor 的后置接口。

destory() :先执行 DisposableBean 接口,再执行配置的 destroyMethod()。

对于 populateBean(),里面的核心其实是对象的依赖注入,这里也是常考的知识点,比如循环依赖,大家如果对这块也感兴趣,可以和我交流。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2956

    浏览量

    104531
  • 编程
    +关注

    关注

    88

    文章

    3587

    浏览量

    93578
  • spring
    +关注

    关注

    0

    文章

    338

    浏览量

    14307
  • IOC
    IOC
    +关注

    关注

    0

    文章

    28

    浏览量

    10087

原文标题:阿里云面试:Spring 中 Bean 的生命周期是怎样的?

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于Rust语言中的生命周期

    Rust是一门系统级编程语言具备高效、安和并发等特,而生命周期是这门语言中比较重要的概念之一。在这篇教程,我们会了解什么是命周期、为什么需要生命周期、如何使用
    的头像 发表于 09-19 17:03 866次阅读

    Spring-10-bean生命周期

    spring
    电子学习
    发布于 :2023年01月07日 16:48:18

    09-SpringBean生命周期总结

    spring
    电子学习
    发布于 :2023年01月14日 09:55:54

    AutoScaling 生命周期挂钩功能

    实例是伸缩组弹出来的不是手动添加的),然后释放实例(如果 ECS 实例是伸缩组弹出来的不是手动添加的),并将实例从伸缩组移出。LifecycleHook 通知方式如果生命周期挂钩配置了通知对象,那么
    发表于 06-27 17:13

    HarmonyOS应用开发-PageAbility生命周期

    pageAbility的生命周期如下图所示:在代码通过调用下列方法实现生命周期操作:onShow() :Ability由后台不可见状态切换到前台可见状态调用onShow方法,此时用户在屏幕可以看到
    发表于 10-17 11:11

    在S32G2 RM中有“生命周期”,生命周期的完整含义是什么?

    在S32G2 RM,有“生命周期”。生命周期的完整含义是什么,我们应该如何使用它?
    发表于 04-23 10:37

    一文读懂Android Activity生命周期

    正常情况下Activity的生命周期: Activity的生命周期大概可以归为三部分 整个的生命周期:onCreate()可以设置所有的“全局”状态, onDestory()可以释放所有的资源 可见
    发表于 05-30 01:03 1566次阅读

    基于延长WSN生命周期的LEACH算法的改进

    基于延长WSN生命周期的LEACH算法的改进(开关电源技术与设计pdf百度云)-基于延长WSN生命周期的LEACH算法的改进                    
    发表于 09-15 11:17 14次下载
    基于延长WSN<b class='flag-5'>生命周期</b>的LEACH算法的改进

    bean放入Spring容器中有哪些方式

    bean放入Spring容器中有哪些方式?
    的头像 发表于 09-19 15:25 692次阅读

    Vue入门Vue的生命周期

    .生命周期 4.1生命周期是什么 Vue的生命周期, 就是Vue实例从创建到销毁的过程.
    的头像 发表于 02-06 16:16 840次阅读
    Vue入门Vue的<b class='flag-5'>生命周期</b>

    编译器的标准生命周期

    编译器的标准生命周期
    发表于 03-14 19:06 0次下载
    编译器的标准<b class='flag-5'>生命周期</b>

    编译器的标准生命周期

    编译器的标准生命周期
    发表于 07-05 19:32 0次下载
    编译器的标准<b class='flag-5'>生命周期</b>

    数据包的生命周期

    电子发烧友网站提供《数据包的生命周期.pdf》资料免费下载
    发表于 10-13 14:44 0次下载

    鸿蒙开发:【PageAbility的生命周期

    PageAbility生命周期是PageAbility被调度到INACTIVE、ACTIVE、BACKGROUND等各个状态的统称。PageAbility生命周期流转及状态说明见如下图1、表1所示。
    的头像 发表于 06-17 10:05 661次阅读
    鸿蒙开发:【PageAbility的<b class='flag-5'>生命周期</b>】

    鸿蒙开发组件:DataAbility的生命周期

    应用开发者可以根据业务场景实现data.js/data.ets生命周期相关接口。DataAbility生命周期接口说明见下表。
    的头像 发表于 06-20 09:39 390次阅读