spring源碼系列-beanDefinition(子路,Java高級面試手册

程序員soto 2021-09-19 20:38:16 阅读数:720

spring 系列 beandefinition 子路 java

Test.java
public class Test{
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Appconfig.class);
ac.refresh();
}
}


上述代碼裏面有X和Y兩個類(下面有筆者運行這些代碼的結果截圖),X被注解了,Y沒注解,並且在X當中有個構造方法一旦X被實例化便會打印`"X Constructor"`;而且在main方法的最開始打印了`"start"`按照上面筆者的理論spring首先會掃描X繼而把X解析稱為一個beanDefinition對象放到map;筆者在spring的源碼`org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors`方法上打了一個斷點(`invokeBeanFactoryPostProcessors`方法裏面就完成了上述所說的掃描解析功能),當應用程序啟動的時候首先會打印```start```,繼而啟動spring容器,然後調用```invokeBeanFactoryPostProcessors```方法,在沒有執行該方法之前查看```beanDefintionMap```當中並沒有key為"x"的元素,說明X並沒有被掃描,然後繼續執行,當執行完```invokeBeanFactoryPostProcessors```方法時候再次查看```beanDefintionMap```可以看到map當中多了一個key為"x"的元素,其對應的value就是一個beanDefintion的對象,最後查看後臺發現還沒有打印`"X Constructor"`這說明這個時候X並沒有被實例化,這個例子說明spring是先把類掃描出來解析稱為一個beanDefintion對象,然後put到```beanDefintionMap```後面才會去實例化X,至於這個```beanDefintionMap```後面的文章我會詳細講解,本文讀者只需知道他是一個專門來存放beanDefinition的集合即可
![spring源碼系列-beanDefinition(子路,Java高級面試手册_後端](https://img-blog.csdnimg.cn/20191018194111708.gif)
④當spring把類所對應的beanDefintion對象存到map之後,spring會調用程序員提供的bean工廠後置處理器。什麼叫bean工廠後置處理器?在spring的代碼級別是用一個接口來錶示BeanFactoryPostProcessor,只要實現這個接口便是一個bean工廠後置處理器了,BeanFactoryPostProcessor的詳細源碼解析後面文章再來分析,這裏先說一下他的基本作用。BeanFactoryPostProcessor接口在spring內部也有實現,比如第①步當中完成掃描功能的類`ConfigurationClassPostProcessor`便是一個spring自己實現的bean工廠後置處理器,這個類筆者認為是閱讀spring源碼當中`最重要`的類,沒有之一;他完成的功能太多了,以後我們一一分析,先看一下這個類的類結構圖。
![spring源碼系列-beanDefinition(子路,Java高級面試手册_程序員_02](https://s2.51cto.com/images/20210919/1632054540421224.jpg)
`ConfigurationClassPostProcessor`實現了很多接口,和本文有關的只需關注兩個接口`BeanDefinitionRegistryPostProcessor`和`BeanFactoryPostProcessor`;但是由於`BeanDefinitionRegistryPostProcessor`是繼承了`BeanFactoryPostProcessor`所以讀者也可以理解這是一個接口,但是筆者更加建議你理解成兩個接口比較合適,因為spring完成上述①②③步的功能就是調用`BeanDefinitionRegistryPostProcessor`的`postProcessBeanDefinitionRegistry`方法完成的,到了第④步的時候spring是執行`BeanFactoryPostProcessor`的`postProcessBeanFactory`方法;這裏可能說的有點繞,大概意思spring完成①②③的功能是調用`ConfigurationClassPostProcessor`的`BeanDefinitionRegistryPostProcessor`的`postProcessBeanDefinitionRegistry`方法,到了第④步spring首先會調用`ConfigurationClassPostProcessor`的`BeanFactoryPostProcessor`的`postProcessBeanFactory`的方法,然後在調用程序員提供的`BeanFactoryPostProcessor`的`postProcessBeanFactory`方法,所以上圖當中第④步我畫的是紅色虛線,因為第④步可能沒有(如果程序員沒有提供自己的`BeanFactoryPostProcessor`;當然這裏一定得說明,即使程序員沒有提供自己擴展的`BeanFactoryPostProcessor`,spring也會執行內置的`BeanFactoryPostProcessor`也就是`ConfigurationClassPostProcessor`,所以上圖畫的並不標准,少了一步;即spring執行內置的`BeanFactoryPostProcessor`;
重點:我們用自己的話總結一下`BeanFactoryPostProcessor`的執行時機(不管內置的還是程序員提供)————**Ⅰ**:如果是直接實現`BeanFactoryPostProcessor`的類是在spring完成掃描類之後(所謂的掃描包括把類變成beanDefinition然後put到map之中),在實例化bean(第⑤步)之前執行;**Ⅱ**如果是實現`BeanDefinitionRegistryPostProcessor`接口的類;誠然這種也叫bean工廠後置處理器他的執行時機是在執行直接實現`BeanFactoryPostProcessor`的類之前,和掃描(上面①②③步)是同期執行;假設你的程序擴展一個功能,需要在**這個時期**做某個功能則可以實現這個接口;但是筆者至今沒有遇到這樣的需求,如果以後遇到或者看到再來補上;(說明一下當筆者發錶這篇博客有一個讀者聯系筆者說他看到了一個主流框架就是擴展了`BeanDefinitionRegistryPostProcessor`類,瞬間筆者如久旱遇甘霖,光棍遇寡婦;遂立馬請教;那比特讀者說mybatis的最新代碼裏面便是擴展這個類來實現的,筆者記得以前mybatis是擴展沒有用到這個接口,後來筆者看了一下最新的mybatis源碼確實如那比特讀者所言,在下一篇筆者分析主流框架如何擴展spring的時候更新)
![spring源碼系列-beanDefinition(子路,Java高級面試手册_Java_03](https://s7.51cto.com/images/20210919/1632054540687435.jpg)
> 這裏再次囉嗦一下,這一段比較枯燥和晦澀,但是非常重要,如果想做到精讀spring源碼這一段尤為重要,建議讀者多看多理解;
那麼第④步當中提到的執行程序員提供的`BeanFactoryPostProcessor`到底有什麼意義呢?程序員提供`BeanFactoryPostProcessor`的場景在哪裏?有哪些主流框架這麼幹過呢?
首先回答第一個問題,意義在哪裏?可以看一下這個接口的方法簽名
```powershell
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException;

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

其實討論這個方法的意義就是討論BeanFactoryPostProcessor的作用或者說意義,參考這個方法的一句javadoc

Modify the application context’s internal bean factory after its standard initialization
在應用程序上下文的標准初始化之後修改它的內部bean工廠

再結合這個方法的執行時機和這段javadoc我們可以理解bean工廠的後置處理器(這裏只討論直接實現BeanFactoryPostProcessor的後置處理器,不包括實現BeanDefinitionRegistryPostProcessor的後置處理器)其實是spring提供的一個擴展點(spring提供很多擴展點,學習spring源碼的一個非常重要的原因就是要學會這些擴展點,以便對spring做二次開發或者寫出優雅的插件),可以讓程序員幹預bean工廠的初始化過程(重點會考);這句話最重要的幾個字是初始化過程,注意不是實例化過程 ;初始化和實例化有很大的區別的,特別是在讀spring源碼的時候一定要注意這兩個名詞;翻開spring源碼你會發現整個容器初始化過程就是spring各種後置處理器調用過程;而各種後置處理器當中大體分為兩種;一種關於實例化的後置處理器一種是關於初始化的後置處理器,這裏不是筆者臆想出來的,如果讀者熟悉spring的後置處理器體系就可以從spring的後置處理器命名看出來spring對初始化和實例化是有非常大的區分的。

說白了就是——beanFactory怎麼new出來的(實例化)BeanFactoryPostProcessor是幹預不了的,但是beanFactory new出來之後各種屬性的填充或者修改(初始化)是可以通過BeanFactoryPostProcessor來幹預;可以看到BeanFactoryPostProcessor裏唯一的方法postProcessBeanFactory中唯一的參數就是一個標准的beanFactory對象——ConfigurableListableBeanFactory;既然spring在調用postProcessBeanFactory方法的時候把已經實例化好的beanFactory對象傳過來了,那麼自然而然我們可以對這個beanFactory肆意妄為了;

雖然肆意妄為聽起來很酷,實則很多人會很迷茫;就相當於現在送給了你一輛奧迪A6(筆者的dream car!重點會考)告訴你可以對這輛車肆意妄為,可你如果只是會按按喇叭,那就對不起贈送者的一番美意了;其實你根本不知道可以在午夜開車這輛車去長沙的解放西路轉一圈,繼而會收貨很多意外的“愛情”;筆者舉這個例子就是想說當你拿到beanFactory對象的時候不能只會sout,那不叫肆意妄為;我們可以幹很多事情,但是你必須要了解beanFactory的特性或者beanFactory的各種api,但是beanFactory這個對象太複雜了,這裏不適合展開討論,與本文相關的只要知道上述我們講到的那個beanDefintionMap(存儲beanDefintion的集合)就定義在beanFactory當中;而且他也提供額api供程序員來操作這個map,比如可以修改這個map當中的beanDefinition對象,也可以添加一個beanDefinition對象到這個map當中;看一段代碼

@Component
public class TestBeanFactoryPostPorcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//轉換為子類,因為父類沒有添加beanDefintion對象的api
DefaultListableBeanFactory defaultbf =
(DefaultListableBeanFactory) beanFactory;
//new一個Y的beanDefinition對象,方便測試動態添加
GenericBeanDefinition y= new GenericBeanDefinition();
y.setBeanClass(Y.class);
//添加一個beanDefinition對象,原本這個Y沒有被spring掃描到
defaultbf.registerBeanDefinition("y", y);
//得到一個已經被掃描出來的beanDefintion對象x
//因為X本來就被掃描出來了,所以是直接從map中獲取
BeanDefinition x = defaultbf.getBeanDefinition("x");
//修改這個X的beanDefintion對象的class為Z
//原本這個x代錶的class為X.class;現在為Z.class
x.setBeanClassName("com.enjoy.beanDefinition.Z");
}
}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

項目裏面有三個類X,Y,Z其中只有X加了@Component注解;也就是當代碼執行到上面那個方法的時候只掃描到了X;beanFactory裏的beanDefinitionMap當中也只有X所對應的beanDefinition對象;筆者首先new了一個Y所對應的beanDefinition對象然後調用registerBeanDefinition("y", y);把y對應的beanDefinition對象put到beanDefinitionMap,這是演示動態添加一個自己實例化的beanDefinition對象;繼而又調用getBeanDefinition("x")得到一個已經存在的beanDefinition對象,然後調用x.setBeanClassName("Z");把x所對應的beanDefinition對象所對應的class改成了Z,這是演示動態修改一個已經掃描完成的beanDefinition對象;

測試代碼如下:

總結

面試難免讓人焦慮不安。經曆過的人都懂的。但是如果你提前預測面試官要問你的問題並想出得體的回答方式,就會容易很多。

此外,都說“面試造火箭,工作擰螺絲”,那對於准備面試的朋友,你只需懂一個字:刷!

給我刷刷刷刷,使勁兒刷刷刷刷刷!今天既是來談面試的,那就必須得來整點面試真題,這不花了我整28天,做了份“Java一線大廠高崗面試題解析合集:JAVA基礎-中級-高級面試+SSM框架+分布式+性能調優+微服務+並發編程+網絡+設計模式+數據結構與算法等”

spring源碼系列-beanDefinition(子路,Java高級面試手册_後端_04

 CodeChina開源項目:【一線大廠Java面試題解析+核心總結學習筆記+最新講解視頻】

且除了單純的刷題,也得需准備一本【JAVA進階核心知識手册】:JVM、JAVA集合、JAVA多線程並發、JAVA基礎、Spring 原理、微服務、Netty與RPC、網絡、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB、Cassandra、設計模式、負載均衡、數據庫、一致性算法、JAVA算法、數據結構、加密算法、分布式緩存、Hadoop、Spark、Storm、YARN、機器學習、雲計算,用來查漏補缺最好不過。

spring源碼系列-beanDefinition(子路,Java高級面試手册_後端_05

版权声明:本文为[程序員soto]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/09/20210919203815865u.html