作者:小傅哥
部落格:https://bugstack.cn
沉澱、分享、成長,讓自己和他人都能有所收穫!?
一、前言
你提出問題,就要給出解決方案!
最近有粉絲小夥伴反饋,與自己的上級溝通總是遇到障礙,感覺不被理解。大部分時候他提出來的事情都可能會被領導說:“我沒get到你的點”、“你想做的這個專案沒有業務價值”、“你提出問題,就要給出解決方案”,等等諸如此類的回答。
鑑於具體情況要具體分析,可能我們並不一定能判斷出是誰的問題,導致在每次的交談中出現的分歧。可能是leader有leader的苦衷和視角,也可能是員工有員工的理解和想法,所以最終沒有達成一致。
但就帶團隊來講,有效溝通很重要。就像:如果你說的都對,那我為什麼和你爭吵呢?與其壓制遇到的矛盾點,不如都攤開了聊,誰的視角和心胸更大,誰就多有一些同理心。
如果尖銳的批評完全消失,溫和的批評將會變得刺耳。
如果溫和的批評也不被允許,沉默將被認為居心叵測。
如果沉默也不再允許,讚揚不夠賣力將是一種罪行。
如果只允許一種聲音存在,那麼,唯一存在的那個聲音就是謊言。
二、面試題
謝飛機,小記!
,總感覺 Spring 也沒啥看的,怎麼面試官一問就能問出花?
面試官:Spring 的 getBean 中,transformedBeanName 的作用是什麼?
謝飛機:不知道呀,看單詞意思好像是改變Bean名稱。
面試官:那這麼說,你的 Bean 如果有 alias 別名,Spring 在獲取 Bean 時候要怎麼處理?
謝飛機:這!
面試官:那如果用了 depends-on 呢?
謝飛機:啊, 我沒用過 depends-on 我不知道!
面試官:那你除錯程式碼時候,看見過BeanName前面有 & 的情況嗎,為啥會出現?
謝飛機:我不配知道!再見!
三、Bean 的獲取過程
對於剛接觸看 Spring 原始碼的夥伴來說,可能很疑惑於怎麼就獲取一個 Bean 就這麼多流程呢?
- 可能有 Bean 可能有別名、可能有依賴、也可能是被 BeanFactory 包裝過,所以會有 transformedBeanName 來處理這些差異化行為。
- 有沒有迴圈依賴、有沒有父工廠、是單例還是原型、是懶載入還是預載入、在不在緩衝區,所以就有各種組合判斷來做不同的流程。
- 提早暴漏物件、三級快取、後置標記清楚,所有的優化處理都是為了讓整個 Bean 的獲取更加高效。
所以,它為了適應各類的需求,變得越來越複雜了。而這部分知識的深入學習絕對不只是為了應付八股文,更多的是考慮到在日常的 Spring 使用中遇到複雜問題時有沒有一個大致知曉的流程,可以快速定位問題,以及此類需求的技術實現方案是否能在以後的應用開發中起到一定的指導作用,因為它是一種設計方案的具體實現。
1. getBean 核心流程圖
- 整張圖就是 getBean 過程中涉及到的類和核心流程用到的方法以及操作的內容。如果你能把整張圖全理解了,那麼基本也就看懂了 getBean 的全過程。
- 本張圖可能會因為網路壓縮變得不清晰,可以通過關注公眾號:bugstack蟲洞棧,回覆:
圖稿
,獲取。
接下來,我們就依次的把關於獲取 Bean 例項的重點程式碼列舉出來做分析,讀者夥伴也可以結合流程圖一起看,這樣會更方便理解。
2. getBean 從哪開始讀原始碼
@Test
public void test_getBean() {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
UserDao userDao = beanFactory.getBean("userDao", UserDao.class);
logger.info("獲取 Bean:{}", userDao);
}
- 在日常應用到 Spring 的開發中基本都是基於註解,幾乎不會自己去使用
beanFactory.getBean
的方式去獲取一個 Bean 例項。 - 所以在你學習的時候如果找不到檢視 getBean 原始碼的入口,也不方便除錯熟悉原始碼時,可以寫這樣一個單元測試類,點入到 getBean 就可以閱讀原始碼了。
3. getBean 原始碼全域性預覽
原始碼位置:AbstractBeanFactory -> getBean() -> doGetBean()
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
// getBean 就像你的領導其實沒做啥,都在 doGetBean 裡
return doGetBean(name, requiredType, null, false);
}
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
// 處理別名BeanName、處理帶&符的工廠BeanName
final String beanName = transformedBeanName(name);
Object bean;
// 先嚐試從快取中獲取Bean例項,這個位置就是三級快取解決迴圈依賴的方法
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
// 1. 如果 sharedInstance 是普通的 Bean 例項,則下面的方法會直接返回
// 2. 如果 sharedInstance 是工廠Bean型別,則需要獲取 getObject 方法,可以參考關於 FactoryBean 的實現類
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// 迴圈依賴有三種,setter注入、多例項和建構函式,Spring 只能解決 setter 注入,所以這裡是 Prototype 則會丟擲異常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 1. 父 bean 工廠存在
// 2. 當前 bean 不存在於當前bean工廠,則到父工廠查詢 bean 例項
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// 獲取 name 對應的 beanName,如果 name 是以 & 開頭,則返回 & + beanName
String nameToLookup = originalBeanName(name);
// 根據 args 引數是否為空,呼叫不同的父容器方法獲取 bean 例項
if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
// 1. typeCheckOnly,用於判斷呼叫 getBean 方法時,是否僅是做型別檢查
// 2. 如果不是隻做型別檢查,就會呼叫 markBeanAsCreated 進行記錄
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// 從容器 getMergedLocalBeanDefinition 獲取 beanName 對應的 GenericBeanDefinition,轉換為 RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 檢查當前建立的 bean 定義是否為抽象 bean 定義
checkMergedBeanDefinition(mbd, beanName, args);
// 處理使用了 depends-on 註解的依賴建立 bean 例項
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
// 監測是否存在 depends-on 迴圈依賴,若存在則會丟擲異常
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// 註冊依賴記錄
registerDependentBean(dep, beanName);
try {
// 載入 depends-on 依賴(dep 是 depends-on 縮寫)
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
// 建立單例 bean 例項
if (mbd.isSingleton()) {
// 把 beanName 和 new ObjectFactory 匿名內部類傳入回撥
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
// 建立 bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// 建立失敗則銷燬
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// 建立其他型別的 bean 例項
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 如果需要型別轉換,這裡會進行操作
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
// 返回 Bean
return (T) bean;
}
綜上基本就是 getBean 過程涉及到的核心處理方法,基本包括;
- transformedBeanName,處理別名BeanName、處理帶&符的工廠BeanName。
- getSingleton,先嚐試從快取中獲取Bean例項,這個位置就是三級快取解決迴圈依賴的方法。
- getObjectForBeanInstance,如果 sharedInstance 是普通的 Bean 例項,則下面的方法會直接返回。另外 sharedInstance 是工廠Bean型別,則需要獲取 getObject 方法,可以參考關於 FactoryBean 的實現類。
- isPrototypeCurrentlyInCreation,迴圈依賴有三種,setter注入、多例項和建構函式,Spring 只能解決 setter 注入,所以這裡是 Prototype 則會丟擲異常。
- getParentBeanFactory,父 bean 工廠存在,當前 bean 不存在於當前bean工廠,則到父工廠查詢 bean 例項。
- originalBeanName,獲取 name 對應的 beanName,如果 name 是以 & 開頭,則返回 & + beanName
- args != null,根據 args 引數是否為空,呼叫不同的父容器方法獲取 bean 例項
- !typeCheckOnly,typeCheckOnly,用於判斷呼叫 getBean 方法時,是否僅是做型別檢查,如果不是隻做型別檢查,就會呼叫 markBeanAsCreated 進行記錄
- mbd.getDependsOn,處理使用了 depends-on 註解的依賴建立 bean 例項
- isDependent,監測是否存在 depends-on 迴圈依賴,若存在則會丟擲異常
- registerDependentBean,註冊依賴記錄
- getBean(dep),載入 depends-on 依賴(dep 是 depends-on 縮寫)
- mbd.isSingleton(),建立單例 bean 例項
- mbd.isPrototype(),建立其他型別的 bean 例項
- return (T) bean,返回 Bean 例項
4. beanName 轉換操作
處理 & 符:transformedBeanName() -> BeanFactoryUtils.transformedBeanName(name)
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}
- 使用 FactoryBean 建立出的物件,會在 DefaultListableBeanFactory 初始化的時候,使用 getBean(FACTORY_BEAN_PREFIX + beanName) 給 beanName 加上 &
(String FACTORY_BEAN_PREFIX = "&")
- 這裡是使用 while 迴圈逐步的把 & 去掉,只要擷取首個字元是 & 符,就繼續迴圈擷取。
&&&userService -> &&userService -> &userService -> userService
別名轉換:transformedBeanName() -> canonicalName
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
<bean id="userService" class="org.itstack.interview.UserService"/>
<alias name="userService" alias="userService-alias01"/>
<alias name="userService-alias01" alias="userService-alias02"/>
- 首先 Spring 對 Bean 的存放並不會使用別名作為Map中的key,所以遇到所有別名獲取 Bean 都需要查到對應原來名字,才可以。如果你知道這個事,是不遇到此類問題時,就知道從哪下手查了
- do...while 迴圈會依次像鏈條一樣不斷的尋找別名對應的名稱,直到當前這個名稱沒有別名了,就返回對應 BeanName
5. depends-on 依賴 Bean
AbstractBeanFactory -> isDependent(beanName, dep) -> DefaultSingletonBeanRegistry
protected boolean isDependent(String beanName, String dependentBeanName) {
synchronized (this.dependentBeanMap) {
return isDependent(beanName, dependentBeanName, null);
}
<bean id="userService" class="org.itstack.interview.UserService" depends-on="userDao"/>
<bean id="userDao" class="org.itstack.interview.UserDao"/>
- isDependent 處理的是使用了 depends-on 配置的 Bean 定義。
private boolean isDependent(String beanName, String dependentBeanName, Set<String> alread
if (alreadySeen != null && alreadySeen.contains(beanName)) {
return false;
}
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
return false;
}
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<String>();
}
alreadySeen.add(beanName);
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true;
}
}
return false;
}
- alreadySeen != null,監測已經依賴的 Bean
- canonicalName,處理別名配置,找到最原來是的 BeanName
- Set
dependentBeans,獲取依賴的 Bean 集合 - for 迴圈遞迴檢測依賴的 Bean,並新增到 alreadySeen 中
AbstractBeanFactory -> registerDependentBean(dep, beanName) -> DefaultSingletonBeanRegistry
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
dependentBeans = new LinkedHashSet<String>(8);
this.dependentBeanMap.put(canonicalName, dependentBeans);
}
dependentBeans.add(dependentBeanName);
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName
if (dependenciesForBean == null) {
dependenciesForBean = new LinkedHashSet<String>(8);
this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
}
dependenciesForBean.add(canonicalName);
}
}
- canonicalName(beanName),獲取原始的 beanName
- synchronized (this.dependentBeanMap),新增 <canonicalName, dependentBeanName> 到 dependentBeanMap 中
- synchronized (this.dependenciesForBeanMap),新增 <dependentBeanName, canonicalName> 到 dependenciesForBeanMap 中
最後:getBean(dep),就可以獲取到 depends-on 依賴的 Bean 了
6. 處理單例項 Bean
AbstractBeanFactory -> mbd.isSingleton()
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
- 這一部分是使用 beanName 和 singletonFactory 匿名內部類傳入等待回撥的方式建立單例項 Bean 例項
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<Exception>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
- this.singletonObjects.get(beanName),先嚐試從快取池中獲取物件,沒有就繼續往下執行
- beforeSingletonCreation(beanName),標記當前 bean 被建立,如果有建構函式注入的迴圈依賴會報錯
- singletonObject = singletonFactory.getObject(),建立 bean 過程就是呼叫 createBean() 方法
- afterSingletonCreation(beanName),最後把標記從集合中移除
- addSingleton(beanName, singletonObject),新建立的會加入快取集合
7. 從快取中獲取 bean 例項
doCreateBean -> if (earlySingletonExposure) -> getSingleton(beanName, false)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從 singletonObjects 獲取例項,singletonObjects 中快取的例項都是完全例項化好的 bean,可以直接使用
Object singletonObject = this.singletonObjects.get(beanName);
// 如果 singletonObject 為空,則沒有建立或建立中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 加鎖
synchronized (this.singletonObjects) {
// 單例快取池中,沒有當前beanName
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 加入到三級快取,暴漏早期物件用於解決三級快取
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
- 其實這一段程式碼主要就是使用三級快取解決set注入迴圈依賴的,後面會單獨列一個章節對迴圈依賴做相關實驗驗證
- singletonObjects,用於存放初始化好的 bean 例項。
- earlySingletonObjects,用於存放初始化中的 bean,來解決迴圈依賴。
- singletonFactories,用於存放 bean 工廠,bean 工廠所生成的 bean 還沒有完成初始化 bean。
8. FactoryBean 中獲取 bean 例項
AbstractBeanFactory -> getObjectForBeanInstance(sharedInstance, name, beanName, null)
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
// 如果 beanName 以 & 開頭,但 beanInstance 卻不是 FactoryBean,則會丟擲異常
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
}
// 這裡判斷就是這個 bean 是不是 FactoryBean,不是就直接返回了
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
return beanInstance;
}
Object object = null;
if (mbd == null) {
// 如果 mbd 為空,則從快取載入 bean(FactoryBean 生成的單例 bean 例項會快取到 factoryBeanObjectCache 集合中,方便使用)
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 到這,beanInstance 是 FactoryBean 型別,所以就強轉了
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
// mbd 為空且判斷 containsBeanDefinition 是否包含 beanName
if (mbd == null && containsBeanDefinition(beanName)) {
// 合併 BeanDefinition
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 呼叫 getObjectFromFactoryBean 獲取例項
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
- (!(beanInstance instanceof FactoryBean),這裡判斷就是這個 bean 是不是 FactoryBean,不是就直接返回了
- 如果 mbd 為空,則從快取載入 bean(FactoryBean 生成的單例 bean 例項會快取到 factoryBeanObjectCache 集合中,方便使用)
- 呼叫 getObjectFromFactoryBean 獲取例項,這裡會包括一部分對單例以及非單例的處理,以及最終返回 factory.getObject(); 對應的 Bean 例項
四、測試案例
1. 別名
<bean id="userService" class="org.itstack.interview.UserService"/>
<!-- 起個別名 -->
<alias name="userService" alias="userService-alias01"/>
<!-- 別名的別名 -->
<alias name="userService-alias01" alias="userService-alias02"/>
@Test
public void test_alias() {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-alias.xml");
UserService userService = beanFactory.getBean("userService-alias02", UserService.class);
logger.info("獲取 Bean 通過別名:{}", userService);
}
- 在單元測試 getBean 的時候,會看到它會把別名逐步處理掉,最終獲取到原有的 BeanName
2. 依賴
<bean id="userService" class="org.itstack.interview.UserService" depends-on="userDao"/>
<bean id="userDao" class="org.itstack.interview.UserDao"/>
@Test
public void test_depends_on() {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-depends-on.xml");
UserService userService = beanFactory.getBean(UserService.class, "userService");
logger.info("獲取 Bean:{}", userService.getUserDao());
}
- 涉及到依賴會走到 dependsOn != null 下,獲取到依賴的 Bean 例項。
3. BeanFactory
<bean id="userDao" class="org.itstack.interview.MyFactoryBean"/>
@Test
public void test_factory_bean() {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config-factory-bean.xml");
UserDao userDao = beanFactory.getBean("userDao", UserDao.class);
logger.info("獲取 Bean:{}", userDao);
}
- 實現 FactoryBean 的類會需要實現 getObject 方法,所有此類的 Bean 最終都是獲取 getObject
五、總結
- 到這裡關於 Spring IOC 獲取 Bean 的核心流程基本就全部介紹完了,整個篇章讓我們看到獲取一個 Bean 的流程也是非常複雜的,涉及到了非常多的分支流程。之所有會有這麼多的流程,就是我們前面介紹到的,因為 Spring 的 Bean 獲取需要滿足很多種情況。
- 在學習的過程可以優先按照 GetBean 流程圖進行梳理,之後對照原始碼按步驟分析,這樣的過程幾乎會消耗你1~2天的時間,但整個過程學習完,基本也就對 GetBean 沒有什麼陌生了。
- 學習幾乎就是一個慢慢磨的過程,就像走迷宮一樣,雖然有時候會走錯路,但那些錯了的路也是知識學習的一部分。在程式設計的學習中不只是看結果,過程是更重要的,學會學習的方式更有意義。