回答:
我們為什么要學習原始碼?
1、知其然知其所以然
2、站在巨人的肩膀上,提高自己的編碼水平
3、應付面試
1.1 Spring原始碼閱讀小技巧
1、類層次藏得太深,不要一個類一個類的去看,遇到方法該進就大膽的進
2、更不要一行一行的去看,看核心點,有些方法并不重要,不要跟它糾纏
3、看不懂的先不看,根據語意和回傳值能知道這個方法達到了啥目的即可
4、只看核心介面(下面標注了重點的地方)和核心代碼,有些地方也許你使用spring以來都沒觸發過
5、debug跟步走,原始碼中給大家標注好了,見到 ”===>“ 就進去
? 進去之前,下一行打個斷點,方便快速回到岔路口
? 進去之前,可以先點方法看原始碼,再debug跟進
6、廣度優先,而非深度優先,先沿著主流程走,了解大概,再細化某些方法
7、認命,spring里多少萬行的代碼,一部書都寫不完,只能學關鍵點
閱讀原始碼目的
加深理解spring的bean加載程序
面試吹牛x
江湖傳說,spring的類關系是這樣的……

1.2 IoC初始化流程與繼承關系
引言
在看原始碼之前需要掌握Spring的繼承關系和初始化
1) IoC容器初始化流程
目標:
1、IoC容器初始化程序中到底都做了哪些事情(宏觀目標)
2、IoC容器初始化是如何實體化Bean的(劃重點,最終目標)
//沒有Spring之前我們是這樣的
User user=new User();
user.xxx();
//有了Spring之后我們是這樣的
<bean id="userService" >
User user= context.getBean("xxx");
user.xxx();
IoC流程簡化圖:
tips:
下面的流轉記不住沒有關系
在剖析原始碼的整個程序中,我們一直會拿著這個圖和原始碼對照

初始化:
1、容器環境的初始化
2、Bean工廠的初始化(IoC容器啟動首先會銷毀舊工廠、舊Bean、創建新的工廠)
讀取與定義
讀取:通過BeanDefinitonReader讀取我們專案中的配置(application.xml)
定義:通過決議xml檔案內容,將里面的Bean決議成BeanDefinition(未實體化、未初始化)
實體化與銷毀
Bean實體化、初始化(注入)
銷毀快取等
擴展點
事件與多播、后置處理器
復雜的流程關鍵點:

重點總結:
1、工廠初始化程序
2、決議xml到BeanDefinition,放到map
3、呼叫后置處理器
4、從map取出進行實體化( ctor.newInstance)
5、實體化后放到一級快取(工廠)
2) 容器與工廠繼承關系
tips:
別緊張,下面的繼承記不住沒有關系
關注顏色標注的幾個就可以
目標:簡單理解ioC容器繼承關系

繼承關系理解:
1、ClassPathXmlApplicationContext最侄訓是到了 ApplicationContext 介面,同樣的,我們也可以使用綠顏色的 FileSystemXmlApplicationContext 和 AnnotationConfigApplicationContext 這兩個類完成容器初始化的作業
2、FileSystemXmlApplicationContext 的建構式需要一個 xml 組態檔在系統中的路徑,其他和 ClassPathXmlApplicationContext 基本上一樣
3、AnnotationConfigApplicationContext 的建構式掃描classpath中相關注解的類,主流程一樣
課程中我們以最經典的 classpathXml 為例,
Bean工廠繼承關系
目標:
ApplicationContext 和 BeanFactory 啥關系?
BeanFactory 和 FactoryBean呢?

總結:
別害怕,上面的繼承關系不用刻意去記住它
其實接觸到的就最下面這個!
1.3 開始搭建測驗專案
四步:
1、新建測驗module專案
首先我們在 Spring 原始碼專案中新增一個測驗專案,點擊 New -> Module... 創建一個 Gradle 的 Java 專案

2、詳細資訊


3、設定gradle

4、完善資訊

在 build.gradle 中添加對 Spring 原始碼的依賴:
compile(project(':spring-context'))

spring-context 會自動將 spring-core、spring-beans、spring-aop、spring-expression 這幾個基礎 jar 包帶進來,
接著,我們需要在專案中創建一個 bean 和組態檔(application.xml)及啟動檔案(Main.java)
介面如下:
package com.spring.test.service;
public interface UserService {
public String getName();
}
實作類
package com.spring.test.impl;
import com.spring.test.service.UserService;
public class UserServiceImpl implements UserService {
@Override
public String getName() {
return "Hello World";
}
}
Main代碼如下
public class Test {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:application.xml");
UserService userService = context.getBean(UserService.class);
System.out.println(userService);
// 這句將輸出: hello world
System.out.println(userService.getName());
}
}
組態檔 application.xml(在 resources 中)配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" />
</beans>
運行
輸出如下
com.spring.test.impl.UserServiceImpl@2aa5fe93
Hello World
1.4 工廠的構建
引言:
接下來,我們就正式講解Spring ioC容器的原始碼
我們的目的:看一下ioC如何幫我們生成物件的
生命周期
1)ApplicationContext入口
參考 IocTest.java
測驗代碼:spring支持多種bean定義方式,為方便大家理解結構,以xml為案例,后面的決議流程一致
ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:${xmlName}.xml");
// (c)從容器中取出Bean的實體,call:AbstractApplicationContext.getBean(java.lang.Class<T>)
//工廠模式(simple)
UserService userService = (UserService) context.getBean("userServiceBeanId");
// 這句將輸出: hello world
System.out.println(userService.getName());
進入到ClassPathXmlApplicationContext的有參構造器
org.springframework.context.support.ClassPathXmlApplicationContext#ClassPathXmlApplicationContext(java.lang.String[], boolean, org.springframework.context.ApplicationContext)
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//繼承結構圖
//1、回傳一個classloader
//2、回傳一個決議器
super(parent);
// 1、獲取環境(系統環境、jvm環境)
// 2、設定Placeholder占位符決議器
// 2、將xml的路徑決議完存盤到陣列
setConfigLocations(configLocations);
//默認為true
if (refresh) {
//核心方法(模板)
refresh();
}
}
重點步驟決議(斷點跟蹤講解)
super方法做了哪些事情
1、super方法:通過點查看父容器與子容器概念
2、super方法:呼叫到頂端,一共5層,每一層都要與講義中的【ioC與Bean工廠類關系繼承】進行對照
3、super方法:在什么地方初始化的類加載器和決議器
setConfigLocations方法做了哪些事情:
1、如何回傳的系統環境和jvm環境
2、路徑的決議
3、設定占位符決議器
進入核心方法refresh
2)預重繪
prepareRefresh()【準備重繪】
// synchronized塊鎖(monitorenter --monitorexit),不然 refresh() 還沒結束,又來個啟動或銷毀容器的操作
synchronized (this.startupShutdownMonitor) {
//1、【準備重繪】【Did four things】
prepareRefresh();
......,略
講解重點(斷點跟蹤、類繼承關系、架構圖講解)
prepareRefresh干了哪些事情
//1、記錄啟動時間/設定開始標志
//2、子類屬性擴展(模板方法)
//3、校驗xml組態檔
//4、初始化早期發布的應用程式事件物件(不重要,僅僅是創建setg物件)
3)創建bean工廠【重點】
【獲得新的bean工廠】obtainFreshBeanFactory()
最終目的就是決議xml,注冊bean定義
關鍵步驟
//1、關閉舊的 BeanFactory
//2、創建新的 BeanFactory(DefaluListbaleBeanFactory)
//3、決議xml/加載 Bean 定義、注冊 Bean定義到beanFactory(未初始化)
//4、回傳全新的工廠
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
4)bean工廠前置操作
【準備bean工廠】prepareBeanFactory(beanFactory);
//1、設定 BeanFactory 的類加載器
//2、設定 BeanFactory 的運算式決議器
//3、設定 BeanFactory 的屬性編輯器
//4、智能注冊
tips
當前代碼邏輯簡單、且非核心
5)bean工廠后置操作
【后置處理器Bean工廠】postProcessBeanFactory(beanFactory) 空方法
tips:子類實作
空方法,跳過
6)工廠后置處理器【重點】
【呼叫bean工廠后置處理器】invokeBeanFactoryPostProcessors(beanFactory);
//呼叫順序一:bean定義注冊后置處理器
//呼叫順序二:bean工廠后置處理器
PostProcessorRegistrationDelegate 類里有詳細注解
tips
invoke方法近200行
關注兩類后置處理器的方法執行步驟和順序
7)bean后置處理器
【注冊bean后置處理器】registerBeanPostProcessors(beanFactory)
//6、【注冊bean后置處理器】只是注冊,但是不會反射呼叫
//功能:找出所有實作BeanPostProcessor介面的類,分類、排序、注冊
registerBeanPostProcessors(beanFactory);
// 核心:查看重要的3步;最終目的都是實作bean后置處理器的注冊
// 第一步: implement PriorityOrdered
// 第二步: implement Ordered.
// 第三步: Register all internal BeanPostProcessors.
8)國際化
【初始化訊息源】國際化問題i18n initMessageSource();
tips:
就加了個bean進去,非核心步驟,跳過
9)初始化事件廣播器
【初始化應用程式事件多路廣播】initApplicationEventMulticaster();
tips:
需要講解觀察者設計模式
重點:就放了個bean進去, 到下面的 listener再聯調,
10)重繪
【重繪】 onRefresh();
空的,交給子類實作:默認情況下不執行任何操作
// 具體的子類可以在這里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
onRefresh();
不重要,跳過
11)注冊監聽器【重點】
【注冊所有監聽器】registerListeners();
測驗代碼參考:MulticastTest
//獲取所有實作了ApplicationListener,然后進行注冊
//1、集合applicationListeners查找
//2、bean工廠找到實作ApplicationListener介面的bean
//3、this.earlyApplicationEvents;
tips:
需要講解觀察者設計模式
重點:演示多播和容器發布
12)完成bean工廠【重點】
【完成bean工廠初始化操作】finishBeanFactoryInitialization(beanFactory);
//【完成bean工廠初始化操作】負責初始化所有的 singleton beans
//此處開始呼叫Bean的前置處理器和后置處理器
finishBeanFactoryInitialization(beanFactory);
講解重點(斷點跟蹤、類繼承關系、架構圖講解)
//1、設定輔助器:例如:決議器、轉換器、類裝載器
//2、實體化
//3、填充
//4、呼叫前置、后置處理器
//核心代碼在 getBean() , 下面單獨講解
13)完成重繪
【完成重繪】
protected void finishRefresh() {
// 1、清除背景關系級資源快取
clearResourceCaches();
// 2、LifecycleProcessor介面初始化
// ps:當ApplicationContext啟動或停止時,它會通過LifecycleProcessor來與所有宣告的bean的周期做狀態更新
// 而在LifecycleProcessor的使用前首先需要初始化
initLifecycleProcessor();
// 3、啟動所有實作了LifecycleProcessor介面的bean
//DefaultLifecycleProcessor,默認實作
getLifecycleProcessor().onRefresh();
// 4、發布背景關系重繪完畢事件到相應的監聽器
//ps:當完成容器初始化的時候,
// 要通過Spring中的事件發布機制來發出ContextRefreshedEvent事件,以保證對應的監聽器可以做進一步的邏輯處理
publishEvent(new ContextRefreshedEvent(this));
// 5、把當前容器注冊到到MBeanServer,用于jmx使用
LiveBeansView.registerApplicationContext(this);
}
tips:
非核心步驟
2 singleton bean 創建【重點】
下面拎出來,重點講 getBean方法,
參考代碼:
先看沒有回圈依賴的情況,普通單例bean的初始化 SinigleTest.java
后面再講回圈依賴
1)呼叫入口
大家都知道是getBean()方法,但是這個方法要注意,有很多呼叫時機
如果你把斷點打在了這里,再點進去getBean,你將會直接從singleton集合中拿到一個實體化好的bean
無法看到它的實體化程序,
可以debug試一下,會發現直接從getSingleTon回傳了bean,這不是我們想要的模樣……

思考一下,為什么呢?
回顧 1.4中的第 12 小節,在bean工廠完成后,會對singleton的bean完成初始化,那么真正的初始化應該發生在那里!
那就需要找到:DefaultListableBeanFactory的第 809 行,那里的getBean
也可以從 1.4的第12小節的入口跟進去,斷點打在這里試試:

這也是我們在上面留下的尾巴,
本小節我們從這里繼續……
2)主流程
小tip:先搞清除3級快取的事
關于bean的三級快取:DefaultSingletonBeanRegistry代碼
/**
* 一級快取:單例(物件)池,這里面的物件都是確保初始化完成,可以被正常使用的
* 它可能來自3級,或者2級
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/**
* 三級快取:單例工廠池,這里面不是bean本身,是它的一個工廠,未來調getObject來獲取真正的bean
* 一旦獲取,就從這里刪掉,進入2級(發生倍訓的話)或1級(沒有倍訓)
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* 二級快取:早期(物件)單例池,這里面都是半成品,只是有人用它提前從3級get出來,把參考暴露出去
* 它里面的屬性可能是null,所以叫早期物件,early!半成品
* 未來在getBean付完屬性后,會調addSingleton清掉2級,正式進入1級
*/
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
傳統叫三級快取里拿bean,其實就是仨map
嚴格意義上,只有single一級快取,其他倆根本算不上是快取
他們只是在生成bean的程序中,暫存過bean的半成品,
就那么稱呼,不必較真
主流程圖很重要!后面的debug會帶著這張圖走

getBean :
入口
doGetBean :
調getSingleton查一下快取看看有沒有,有就回傳,沒有給singleton一個lambda運算式,函式式編程里調下面的createBean拿到新的bean,然后清除3級快取,放入1級快取
createBean :
調這里,一堆檢查后,進入下面
doCreateBean :
真正創建bean的地方: 調建構式初始化 - 放入3級快取 - 決議屬性賦值 - bean后置處理器
3)getSingleton
在DefaultSingletonBeanRegistry里,有三個,作用完全不一樣
//啥也沒干,調下面傳了個true
public Object getSingleton(String beanName)
//從1級快取拿,1級沒有再看情況
//后面的引數如果true,就使用3級升2級回傳,否則直接回傳null
protected Object getSingleton(String beanName, boolean allowEarlyReference)
//1級沒有,通過給的factory創建并放入1級里,清除2、3
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory)
4)bean實體化
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
5)放入三級快取
回圈依賴和aop
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#addSingletonFactory
4)注入屬性
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
真正給bean設定屬性的地方!
7)bean前后置
還記得上面我們自定義的 Bean后置處理器嗎
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean
前、后置的呼叫,在這里
詳細見下圖第3步,很多,了解即可,需要時查一下在相關地方擴展

8)小結
偽代碼,無回圈依賴時,生成bean流程一覽
getBean("A"){
doGetBean("A"){
a = getSingleton("A"){
a = singletonObjects(); //查1級快取,null
if("創建過3級快取"){ //不成立
//忽略
}
return a;
}; // null
if(a == null){
a = getSingleton("A" , ObjectFactory of){
a = of.getObject() -> { //lambda運算式
createBean("A"){
doCreateBean("A"){
createBeanInstance("A"); // A 實體化
addSingletonFactory("A"); // A 放入3級快取
populateBean("A"); // A 注入屬性
initializeBean("A"); // A 后置處理器
} //end doCreateBean("A")
} //end crateBean("A")
} // end lambda A
addSingleton("A" , a) // 清除2、3級,放入1級
} // end getSingleton("A",factory)
} // end if(a == null)
return a;
} //end doGetBean("A")
}//end getBean("A")
3 Spring的回圈依賴
引言
在上面,我們剖析了bean實體化的整個程序
也就是我們的Bean他是單獨存在的,和其他Bean沒有交集和參考
而我們在業務開發中,肯定會有多個Bean相互參考的情況
也就是所謂的回圈依賴
3.1 什么是回圈依賴
簡單回顧下
通俗的講就是N個Bean互相參考對方,最終形成倍訓,

專案代碼介紹如下(測驗類入口: CircleTest.java)
組態檔
<!--回圈依賴BeanA依賴BeanB -->
<bean id="userServiceImplA" >
<property name="userServiceImplB" ref="userServiceImplB"/>
</bean>
<!--回圈依賴BeanB依賴BeanA -->
<bean id="userServiceImplB" >
<property name="userServiceImplA" ref="userServiceImplA"/>
</bean>
userServiceImplA代碼如下
public class UserServiceImplA implements UserService {
private UserServiceImplB userServiceImplB;
public void setUserServiceImplB(UserServiceImplB userServiceImplB) {
this.userServiceImplB = userServiceImplB;
}
@Override
public String getName() {
return "在UserServiceImplA的Bean中" +
"userServiceImplB注入成功>>>>>>>>>"+userServiceImplB;
}
}
userServiceImplB代碼如下
//實作類
public class UserServiceImplB implements UserService {
private UserServiceImplA userServiceImplA;
public void setUserServiceImplA(UserServiceImplA userServiceImplA) {
this.userServiceImplA = userServiceImplA;
}
@Override
public String getName() {
return "在UserServiceImplB的Bean中" +
"userServiceImplA注入成功>>>>>>>>>"+userServiceImplA;
}
入口Main
ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:application.xml");
UserService userService = context.getBean("userServiceImplA",UserService.class);
System.out.println(userService.getName());
輸出如下

3.2 Spring如何解決回圈依賴
假如無法解決回圈依賴
1、Bean無法成功注入,導致業務無法進行
2、產生死回圈(一種假設情景)
1)三級快取變化程序
目標:
只有明白三級快取變化程序,才能知道是如何解決回圈依賴的
略去其他步驟,只看快取變化

變化程序3-1:如下圖:

步驟:
A :... - 走到doCreateBean: 初始化 - 進3級快取 - 注入屬性,發現需要B
B :... - 走到doCreateBean: 初始化 - 進3級快取
1、BeanA經歷gdcd四個方法,走到doCreatebean里在實體化后、注入前放到三級快取
2、放到三級快取后;BeanA在正式的注入的時候,發現有回圈依賴,重復上【1】的步驟
3、最終:BeanA和BeanB都放到了三級快取
變化程序3-2:如下圖:

步驟:
1、BeanB放到三級快取后,這個時候BeanB要開始注入了;
于是,BeanB找到了回圈依賴BeanA后,再從頭執行A的getBean和doGetBean方法;
此處在getSingleton里面(這貨第一次是必經的,但第二次來行為不一樣了)將BeanA設定到了二級快取,并且把BeanA從三級快取移除走了
2、BeanB如愿以償的拿到了A,注入,此時,完成了注入程序;一直到DefaultSingletonBeanRegistry#addSingleton方法后;BeanB從三級快取直接進入一級快取,完成它的使命
3、目前,一級快取有BeanB(里面的BeanA屬性還是空)、二級快取有BeanA 三級快取為空
效果如下
走到這一步,B里面有A,它已完成,
但是很不幸,A里面的B還是null,我們第三步會繼續完成這個設定

思考一下:
如果不用三級,我們直接用2級也能實作,但是3級我們說它是一個Factory,里面可以在創建的前后嵌入我們的代碼,和前后置處理器,Aop之類的操作就發生在這里
而2級存放的是bean實體,沒這么多擴展的可能性,如果僅僅用于bean回圈創建,倒是可以
總結:
1、如果不呼叫后置,回傳的bean和三級快取一樣
2、如果呼叫后置,回傳的就是代理物件
3、這就是三級快取設計的巧妙之處!!!!Map<String, ObjectFactory<?>>
變化程序3-3:如下圖:

步驟:
此時, BeanB里面已經注入了BeanA,它自己完成并進入了一級快取
要注意,它的完成是被動的結果,也就是A需要它,臨時先騰出時間創建了它
接下來,BeanA 還要繼續自己的流程,然后populateBean方法將BeanB注入到自己里
最后,BeanA 進一級快取,洗掉之前的二級
整個流程完成!
大功告成:雙方相互持有對方效果如下:

2)三級快取解決方案總結
簡化版

序列圖

三級快取解決回圈依賴程序(回顧)
1、BeanA經過gdcd方法、放入到3級快取、如果有回圈依賴BeanB,重復執行gdcd方法
2、直到發現了它也需要A,而A前面經歷了一次get操作,將3級快取的BeanA放到2級快取
3、然后2級快取的A注入進BeanB, BeanB完事進一級快取,此時BeanB持有BeanA
3、接下來,繼續完成BeanA剩下的操作,取BeanB填充進BeanA,將BeanA放到一級快取,完成!
偽代碼,回圈依賴流程一覽,都是關鍵步驟,不能再簡化了
建議粘貼到vscode等編輯器里查看,因為……它層級太tmd深了!
getBean("A"){
doGetBean("A"){
a = getSingleton("A"){
a = singletonObjects(); //查1級快取,null
if("創建過3級快取"){ //不成立
//忽略
}
return a;
}; // A第一次,null
if(a == null){
a = getSingleton("A" , ObjectFactory of){
a = of.getObject() -> { //lambda運算式
createBean("A"){
doCreateBean("A"){
createBeanInstance("A"); // A 實體化
addSingletonFactory("A"); // A 放入3級快取
populateBean("A"){
//A 需要B,進入B的getBean
b = getBean("B"){
doGetBean("B"){
b = getSingleton("B"); // B第一次,null
if(b == null){
b = getSingleton("B", ObjectFactory of){
b = of.getObject() -> {
createBean("B"){
doCreateBean("B"){
createBeanInstance("B"); // B 實體化
addSingletonFactory("B"); // B 放入3級快取
populateBean("B"){
//B 需要A,2次進入A的getBean
a = getBean("A"){
doGetBean("A"){
a = getSingleton("A"){
a = singletonObjects(); //查1級快取,null
if("創建過3級快取"){ //成立!
a = singletonFactory.getObject("A"); //取3級快取,生成a
earlySingletonObjects.put("A", a); //放入2級快取
singletonFactories.remove("A"); //移除3級快取
return a;
}
}; // A第二次,不是null,但是半成品,還待在2級快取里
} // end doGetBean("A")
} // end getBean("A")
} // end populate B
initializeBean("B",b); // B后置處理器
} // end doCreateBean B
} // end createBean B
} // end lambda B
// B 創建完成,并且是完整的,雖然它里面的A還是半成品,但不影響它進入1級
addSingleton("B",b) ; // 清除3級快取,進入1級
); // end getSingleton("B",factory)
} // end if(b==null);
return b;
} // end doGetBean("B")
} // end getBean("B")
} // end populateBean("A")
initializeBean("A"); // A 后置處理器
} //end doCreateBean("A")
} //end crateBean("A")
} // end lambda A
addSingleton("A" , a) // 清除2、3級,放入1級
} // end getSingleton("A",factory)
} // end if(a == null)
return a;
} //end doGetBean("A")
}//end getBean("A")
總結
可以發現,通過spring的三級快取完美解決了回圈依賴
Spring處理機制很聰明;它先掃描一遍Bean,先放到一個容器(3級快取待命)
此時也不知道是否存在回圈依賴,先放到三級快取再說
等到設定屬性的時候,取對應的屬性bean去(此時才發現有了回圈依賴) ,在放到第二個容器(2級快取,半成品)
繼續,然后從二級快取拿出進行填充(注入)
填充完畢,將自己放到一級快取(這個bean是被動創建出來的,因為別人需要它,結果它先完成了)
然后不斷回圈外層,處理最原始要創建的那個bean
為什么設計三級?二級快取能否解決回圈依賴?
可以解決,別說2級,1級都行
雖然二級快取能解決回圈依賴,但是aop時會可能會引發問題,三級是一個factory,在里面配備了對應的后置處理器,其中就有我們的aop (后面會講到),如果有人要用它,會在呼叫factory的getObject時生效,生成代理bean而不是原始bean,
如果不這么做,直接創建原始物件注入,可能引發aop失效,
所以spring的3級各有意義:
1級:最終成品
2級:半成品
3級:工廠,備用
在上面的方法getEarlyBeanReference(提前暴露的參考)
回顧下
AbstractAutowireCapableBeanFactory.getEarlyBeanReference
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//回圈所有Bean后置處理器
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
//重點:開始創建AOP代理
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
總結下:
1、如果不呼叫后置處理器,回傳的Bean和三級快取一樣,都是實體化、普通的Bean
2、如果呼叫后置,回傳的就是代理物件,不是普通的Bean了
其實;這就是三級快取設計的巧妙之處
那為什么要2級呢? 不能直接放入1級嗎?
不能!
A-B-A中,第二次A的時候,A還是個半成品,不能放入1級
以上面為例,A在進入2級快取的時候,它里面的B還是個null !
如果放入1級,被其他使用的地方取走,會引發問題,比如空指標
4 IoC用到的那些設計模式
引言:
Spring中使用了大量的設計模式(面試)
4.1 工廠
工廠模式(Factory Pattern)提供了一種創建物件的最佳方式,
工廠模式(Factory Pattern)分為三種
1、簡單工廠
2、工廠方法
3、抽象工廠
1. 簡單工廠模式
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:application.xml");\
UserService userService = context.getBean(UserService.class);
簡單工廠模式對物件創建管理方式最為簡單,因為其僅僅簡單的對不同類物件的創建進行了一層簡單的封裝
定義介面IPhone
public interface Phone {
void make();
}
實作類
public class IPhone implements Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("生產蘋果手機!");
}
}
實作類
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("生產小米手機!");
}
}
定義工廠類并且測驗
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if (phoneType.equalsIgnoreCase("MiPhone")) {
return new MiPhone();
} else if (phoneType.equalsIgnoreCase("iPhone")) {
return new IPhone();
}
return null;
}
//測驗簡單工廠
public static void main(String[] arg) {
PhoneFactory factory = new PhoneFactory();
Phone miPhone = factory.makePhone("MiPhone");
IPhone iPhone = (IPhone) factory.makePhone("iPhone");
}
}

4.2 模板
模板模式(Template Pattern):基于抽象類的,核心是封裝演算法
Spring核心方法refresh就是典型的模板方法
org.springframework.context.support.AbstractApplicationContext#refresh
模板設計模式—
模板方法定義了一個演算法的步驟,并允許子類為一個或多個步驟提供具體實作
//模板模式
public abstract class TemplatePattern {
protected abstract void step1();
protected abstract void step2();
protected abstract void step3();
protected abstract void step4();
//模板方法
public final void refresh() {
//此處也可加入當前類的一個方法實作,例如init()
step1();
step2();
step3();
step4();
}
}
定義子類
//模板模式
public class SubTemplatePattern extends TemplatePattern {
@Override
public void step1() {
System.out.println(">>>>>>>>>>>>>>1");
}
@Override
public void step2() {
System.out.println(">>>>>>>>>>>>>>2");
}
@Override
public void step3() {
System.out.println(">>>>>>>>>>>>>>3");
}
@Override
public void step4() {
System.out.println(">>>>>>>>>>>>>>4");
}
//測驗
public static void main(String[] args) {
TemplatePattern tp = new SubTemplatePattern();
tp.refresh();
}
}
輸出

4.3 觀察者
什么是觀察者模式
觀察者模式(Observer Pattern):當物件間存在一對多關系時,則使用觀察者模式(Observer Pattern),比如,當一個物件被修改時,則會自動通知依賴它的物件,
Spring 的事件機制就是具體的觀察者模式的實作
spring中的多播與事件
AbstractApplicationContext#initApplicationEventMulticaster
AbstractApplicationContext#registerListeners
觀察者模式有哪些角色?
事件 ApplicationEvent 是所有事件物件的父類,繼承JDK的EventObject
事件監聽 ApplicationListener,也就是觀察者物件,繼承自 JDK 的 EventListener,可以監聽到事件;該類中只有一個方法 onApplicationEvent,當監聽的事件發生后該方法會被執行,
事件發布ApplicationContext, 實作事件的發布,
(發布事件)
or=========
Spring中的多播
事件發布 ApplicationEventMulticaster,用于事件監聽器的注冊和事件的廣播,

自定義一個事件MessageSourceEvent并且實作ApplicationEvent介面
//在Spring 中使用事件監聽機制(事件、監聽、發布)
//定義事件
//執行順序
//1、進入到事件源的有引數構造器
//2、發布事件
//3、進入到監聽器類---one
//4、進入到事件源的方法
//5、進入到監聽器類---two
//6、進入到事件源的方法
public class MessageSourceEvent extends ApplicationEvent {
public MessageSourceEvent(Object source) {
super(source);
System.out.println("進入到事件源的有引數構造器");
}
public void print() {
System.out.println("進入到事件源的方法");
}
}
有了事件之后還需要自定義一個監聽用來接收監聽到事件,自定義ApplicationContextListener監聽 需要交給Spring容器管理, 實作ApplicationListener介面并且重寫onApplicationEvent方法,
監聽一
//在Spring 中使用事件監聽機制(事件、監聽、發布)
//監聽類,在spring組態檔中,注冊事件類和監聽類
public class ApplicationContextListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof MessageSourceEvent) {
System.out.println("進入到監聽器類---one");
MessageSourceEvent myEvent = (MessageSourceEvent) event;
myEvent.print();
}
}
}
監聽二
//在Spring 中使用事件監聽機制(事件、監聽、發布)
//監聽類,在spring組態檔中,注冊事件類和監聽類
public class ApplicationContextListenerTwo implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof MessageSourceEvent){
System.out.println("進入到監聽器類---two");
MessageSourceEvent myEvent=(MessageSourceEvent)event;
myEvent.print();
}
}
}
發布事件
//在Spring 中使用事件監聽機制(事件、監聽、發布)
//該類實作ApplicationContextAware介面,得到ApplicationContext物件
// 使用該物件的publishEvent方法發布事件
public class ApplicationContextListenerPubisher implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void publishEvent(ApplicationEvent event) {
System.out.println("發布事件");
applicationContext.publishEvent(event);
}
}
組態檔
<!-- Spirng中的事件>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
<!--<bean id="messageSourceEvent" />-->
<bean id="applicationContextListener" />
<bean id="applicationContextListenerTwo" />
<bean id="applicationContextListenerPubisher" />
<!-- Spirng中的事件>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -->
測驗
//總結 :使用bean工廠發布和使用多播器效果是一樣的
public class Test {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("classpath*:application.xml");
//***************使用spring的多播器發布**********************
ApplicationEventMulticaster applicationEventMulticaster = (ApplicationEventMulticaster) context.getBean("applicationEventMulticaster");
applicationEventMulticaster.multicastEvent(new MessageSourceEvent("測驗..."));
//***************使用BeanFactory的publishEvent發布*********************
// ApplicationContextListenerPubisher myPubisher = (ApplicationContextListenerPubisher)
//context.getBean("applicationContextListenerPubisher");
//myPubisher.publishEvent(new MessageSourceEvent("測驗..."));
}
}
多播發布

工廠發布

總結:
? 1、spring的事件驅動模型使用的是 觀察者模式
2、通過ApplicationEvent抽象類和ApplicationListener介面,可以實作事件處理
3、ApplicationEventMulticaster事件廣播器實作了監聽器的注冊,一般不需要我們實作,只需要顯示的呼叫 applicationcontext.publisherEvent方法即可
? 4、使用bean工廠發布和使用多播器效果是一樣的
本文由
傳智教育博學谷教研團隊發布,如果本文對您有幫助,歡迎
關注和點贊;如果您有任何建議也可留言評論或私信,您的支持是我堅持創作的動力,轉載請注明出處!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/520570.html
標籤:Java
上一篇:對redis的實戰理解
