成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

去字節面試,直接讓人出門左拐:Bean 生命周期都不知道!

開發 架構
IoC 很好地體現了面向對象設計法則之一 —— 好萊塢法則:“別找我們,我們找你”,即由 IoC 容器幫對象找相應的依賴對象并注入,而不是由對象主動去找。

大家好,我是樓仔!

Spring Bean 的生命周期,面試時非常容易問,這不,前段時間就有個粉絲去字節面試,因為不會回答這個問題,一面都沒有過。

如果只講基礎知識,感覺和網上大多數文章沒有區別,但是我又想寫得稍微深入一點。

考慮很多同學不喜歡看源碼,我就把文章分為 2 大部分,前面是基礎知識,主要方便大家面試和學習,后面是源碼部分,對源碼感興趣的同學可以繼續往后面看。

圖片

1. 基礎知識

1.1 什么是 IoC ?

IoC,控制反轉,想必大家都知道,所謂的控制反轉,就是把 new 對象的權利交給容器,所有的對象都被容器控制,這就叫所謂的控制反轉。

IoC 很好地體現了面向對象設計法則之一 —— 好萊塢法則:“別找我們,我們找你”,即由 IoC 容器幫對象找相應的依賴對象并注入,而不是由對象主動去找。

理解好 IoC 的關鍵是要明確 “誰控制誰,控制什么,為何是反轉(有反轉就應該有正轉了),哪些方面反轉了”。

圖片

誰控制誰,控制什么?

傳統 Java SE 程序設計,我們直接在對象內部通過 new 進行創建對象,是程序主動去創建依賴對象。而 IoC 是由專門一個容器來創建這些對象,即由 IoC 容器來控制對象的創建。

  • 誰控制誰?當然是 IoC 容器控制了對象;
  • 控制什么?主要控制了外部資源獲取(不只是對象,比如包括文件等)。

為何是反轉,哪些方面反轉了?

有反轉就有正轉,傳統應用程序是由我們自己在對象中主動控制去直接獲取依賴對象,也就是正轉,而反轉則是由容器來幫忙創建及注入依賴對象。

  • 為何是反轉?因為由容器幫我們查找及注入依賴對象,對象只是被動的接受依賴對象,所以是反轉;
  • 哪些方面反轉了?依賴對象的獲取被反轉了。

1.2 Bean 生命周期

對 Prototype Bean 來說,當用戶 getBean 獲得 Prototype Bean 的實例后,IOC 容器就不再對當前實例進行管理,而是把管理權交由用戶,此后再 getBean 生成的是新的實例。

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

圖片

Bean 生命周期過程:

  • 實例化:第 1 步,實例化一個 Bean 對象;
  • 屬性賦值:第 2 步,為 Bean 設置相關屬性和依賴;
  • 初始化:初始化的階段的步驟比較多,5、6 步是真正的初始化,第 3、4 步為在初始化前執行,第 7 步在初始化后執行,初始化完成之后,Bean 就可以被使用了;
  • 銷毀:第 8~10 步,第 8 步其實也可以算到銷毀階段,但不是真正意義上的銷毀,而是先在使用前注冊了銷毀的相關調用接口,為了后面第 9、10 步真正銷毀 Bean 時再執行相應的方法。

整個執行流程稍微有些抽象,下面我們通過代碼,來演示執行流程。

1.3 執行流程

創建一個 LouzaiBean。

public class LouzaiBean implements InitializingBean, BeanFactoryAware, BeanNameAware, DisposableBean {

/**
* 姓名
*/
private String name;

public LouzaiBean() {
System.out.println("1.調用構造方法:我出生了!");
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
System.out.println("2.設置屬性:我的名字叫"+name);
}

@Override
public void setBeanName(String s) {
System.out.println("3.調用BeanNameAware#setBeanName方法:我要上學了,起了個學名");
}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("4.調用BeanFactoryAware#setBeanFactory方法:選好學校了");
}

@Override
public void afterPropertiesSet() throws Exception {
System.out.println("6.InitializingBean#afterPropertiesSet方法:入學登記");
}

public void init() {
System.out.println("7.自定義init方法:努力上學ing");
}

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

public void destroyMethod() {
System.out.println("10.自定義destroy方法:睡了,別想叫醒我");
}

public void work(){
System.out.println("Bean使用中:工作,只有對社會沒有用的人才放假。。");
}
}

自定義一個后處理器 MyBeanPostProcessor。

public class MyBeanPostProcessor implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("5.BeanPostProcessor.postProcessBeforeInitialization方法:到學校報名啦");
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("8.BeanPostProcessor#postProcessAfterInitialization方法:終于畢業,拿到畢業證啦!");
return bean;
}
}

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

<bean name="myBeanPostProcessor" class="demo.MyBeanPostProcessor" />
<bean name="louzaiBean" class="demo.LouzaiBean"
init-method="init" destroy-method="destroyMethod">
<property name="name" value="樓仔" />
</bean>

測試入口:

public class MyTest {
public static void main(String[] args) {
ApplicationContext context =new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
LouzaiBean louzaiBean = (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 代碼入口

圖片

圖片

這里需要多跑幾次,把前面的 beanName 跳過去,只看 louzaiBean。

圖片

圖片

進入 doGetBean(),從 getSingleton() 沒有找到對象,進入創建 Bean 的邏輯。

圖片

圖片

2.2 實例化

進入 doCreateBean() 后,調用 createBeanInstance()。

圖片

進入 createBeanInstance() 后,調用 instantiateBean()。

圖片

圖片

圖片

圖片

圖片

走進示例 LouzaiBean 的方法,實例化 LouzaiBean。

圖片

2.3 屬性賦值

再回到 doCreateBean(),繼續往后走,進入 populateBean()。

這個方法非常重要,里面其實就是依賴注入的邏輯,不過這個不是我們今天的重點,大家如果對依賴注入和循環依賴感興趣,可以翻閱我之前的文章。

圖片

進入 populateBean() 后,執行 applyPropertyValues()

圖片

進入 applyPropertyValues(),執行 bw.setPropertyValues()

圖片

圖片

圖片

圖片

進入 processLocalProperty(),執行 ph.setValue()。

圖片

圖片

圖片

走進示例 LouzaiBean 的方法,給 LouzaiBean 賦值 name。

圖片

到這里,populateBean() 就執行完畢,下面開始初始化 Bean。

2.4 初始化

我們繼續回到 doCreateBean(),往后執行 initializeBean()。

圖片

圖片

圖片

走進示例 LouzaiBean 的方法,給 LouzaiBean 設置 BeanName。

圖片

回到 invokeAwareMethods()。

圖片

走進示例 LouzaiBean 的方法,給 LouzaiBean 設置 BeanFactory。

圖片

第一次回到 initializeBean(),執行下面邏輯。

圖片

這里需要多循環幾次,找到 MyBeanPostProcessor 的策略方法。

圖片

我們自己定義的后置處理方法。

圖片

第二次回到 initializeBean(),執行下面邏輯。

圖片

圖片

走進示例 LouzaiBean 的方法,執行 afterPropertiesSet()。

圖片

返回 invokeInitMethods(),執行下面邏輯。

圖片

進入 invokeCustomInitMethod(),執行下面邏輯。

圖片

走進示例 LouzaiBean 的方法,執行 init()。

圖片

第三次回到 initializeBean(),執行下面邏輯。

圖片

圖片

我們自己定義的后置處理方法。

圖片

到這里,初始化的流程全部結束,都是圍繞 initializeBean() 展開。

2.4 銷毀

當 louzaiBean 生成后,后面開始執行銷毀操作,整個流程就比較簡單。

圖片

圖片

圖片

圖片

圖片

圖片

圖片

圖片

圖片

圖片

走進示例 LouzaiBean 的方法,執行 destroy()。

圖片

回到 destroy(),執行下面邏輯。

圖片

圖片

圖片

走進示例 LouzaiBean 的方法,執行 destroyMethod()。

圖片

到這里,所有的流程全部結束,文章詳細描述所有的代碼邏輯流轉,你可以完全根據上面的邏輯,自己 debug 一遍。

3. 寫在最后

我們再回顧一下幾個重要的方法:

  • doCreateBean():這個是入口;
  • createBeanInstance():用來初始化 Bean,里面會調用對象的構造方法;
  • populateBean():屬性對象的依賴注入,以及成員變量初始化;
  • initializeBean():里面有 4 個方法,

先執行 aware 的 BeanNameAware、BeanFactoryAware 接口;

再執行 BeanPostProcessor 前置接口;

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

最后執行 BeanPostProcessor 的后置接口。

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

對于 populateBean(),里面的核心其實是對象的依賴注入,這里也是常考的知識點,比如循環依賴,大家如果對這塊也感興趣,可以私下和我交流。

今天的源碼解析就到這,Spring 相關的源碼,還有哪些是大家想學習的呢,可以給樓仔留言。

這篇文章肝了我一個星期,原創不易,大家的點贊和分享,是我繼續創作的最大動力!

參考文章

三分惡的《Spring Bean生命周期,好像人的一生。。》:https://juejin.cn/post/7075168883744718856

責任編輯:武曉燕 來源: 樓仔
相關推薦

2020-05-22 08:11:48

線程池JVM面試

2023-03-30 07:34:10

Linux性能數據結構

2020-12-21 09:00:04

MySQL緩存SQL

2020-12-21 09:44:53

MySQL查詢緩存數據庫

2018-10-17 14:50:08

2021-12-08 11:18:21

Spring Bean面試題生命周期

2022-10-19 23:28:55

Spring生命周期Bean

2024-05-28 07:55:31

SpringBean用域

2022-09-05 07:06:59

BeanSpring

2023-12-28 09:59:37

Spring容器XML

2020-07-29 09:53:09

VSCode編碼工具插件

2025-02-18 00:05:00

2021-11-12 10:05:19

跳表BAT面試

2023-01-13 16:48:48

前端開發JavaScript

2021-07-22 09:28:35

DockerLinux命令

2024-10-22 09:03:35

前端signalAPI

2015-07-08 16:28:23

weak生命周期

2020-02-10 19:34:12

生命周期流程流程圖

2025-02-04 17:33:00

2021-07-26 05:17:39

Linux PosixLinux 系統
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久草在线中文888 | 久久国产综合 | 精品久久影院 | 一区二区三区精品视频 | 亚洲精品福利视频 | 青青草中文字幕 | 天天拍夜夜爽 | 欧美在线亚洲 | 在线观看毛片网站 | 精品欧美一区二区久久久伦 | 精品视频久久久 | 天天干.com| 狠狠狠色丁香婷婷综合久久五月 | 另类二区| 国产不卡视频在线 | 久久精品日产第一区二区三区 | 亚洲人成人一区二区在线观看 | 亚洲国产精品久久 | 色吊丝2 | 天天干天天干 | www成年人视频 | 一级片网站视频 | 日韩av在线中文字幕 | 欧洲视频一区二区 | 午夜视频一区二区三区 | 国产精品大片 | 丝袜一区二区三区 | 一区二区精品在线 | gogo肉体亚洲高清在线视 | 国产探花在线精品一区二区 | 精品视频一区二区三区在线观看 | 国产高清精品一区二区三区 | 中文字幕一区二区三区不卡 | 亚洲色图在线观看 | 国产精品久久久久久久久久免费 | 亚洲高清电影 | 国产综合精品 | 在线一区二区三区 | 亚洲a视频 | 久久精品国产一区二区电影 | 国产精品美女久久久久aⅴ国产馆 |