版本約定面試
本文內容若沒作特殊說明,均基於如下版本:安全
1.85.2.2.RELEASE
複製代碼
上篇文章介紹了代理對象兩個攔截器其中的前者,即 BeanFactoryAwareMethodInterceptor ,它會攔截 setBeanFactory() 方法從而完成給代理類指定屬性賦值。經過第一個攔截器的講解,你可以成功「忽悠」不少面試官了,但仍舊不可以解釋咱們最常使用中的這個疑惑: 爲什麼經過調用@Bean方法最終指向的仍舊是同一個Bean呢?bash
帶着這個疑問,開始本文的陳述。請繫好安全帶,準備發車了...app
根據不一樣的配置方式,展現不一樣狀況。從Lite模式的使用 產生誤區 ,到使用Full模式解決問題,最後引出解釋爲什麼有此效果的緣由分析/源碼解析。ide
配置類:post
public class AppConfig {
@Bean
public Son son() {
Son son = new Son();
System.out.println("son created..." + son.hashCode());
return son;
}
@Bean
public Parent parent() {
Son son = son();
System.out.println("parent created...持有的Son是:" + son.hashCode());
return new Parent(son);
}
}
複製代碼
運行程序:ui
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig appConfig = context.getBean(AppConfig.class);
System.out.println(appConfig);
// bean狀況
Son son = context.getBean(Son.class);
Parent parent = context.getBean(Parent.class);
System.out.println("容器內的Son實例:" + son.hashCode());
System.out.println("容器內Person持有的Son實例:" + parent.getSon().hashCode());
System.out.println(parent.getSon() == son);
}
複製代碼
運行結果:this
son created...624271064
son created...564742142
parent created...持有的Son是:564742142
com.yourbatman.fullliteconfig.config.AppConfig@1a38c59b
容器內的Son實例:624271064
容器內Person持有的Son實例:564742142
false
複製代碼
結果分析:spa
這樣的話, 就出問題了 。問題表如今這兩個方面:3d
這種狀況在生產上是 必定須要避免 ,那怎麼破呢?下面給出Lite模式下使用的正確姿式。
其實這個問題,如今這麼智能的IDE(如IDEA)已經能教你怎麼作了:
按照「指示」,可使用 依賴注入 的方式代替從而避免這種問題,以下:
// @Bean
// public Parent parent() {
// Son son = son();
// System.out.println("parent created...持有的Son是:" + son.hashCode());
// return new Parent(son);
// }
@Bean
public Parent parent(Son son){
System.out.println("parent created...持有的Son是:" + son.hashCode());
return new Parent(son);
}
複製代碼
再次運行程序,結果爲:
son created...624271064
parent created...持有的Son是:624271064
com.yourbatman.fullliteconfig.config.AppConfig@667a738
容器內的Son實例:624271064
容器內Person持有的Son實例:624271064
true
複製代碼
bingo, 完美解決了問題 。若是你堅持使用Lite模式,那麼請注意它的優缺點哦(Full模式和Lite模式的優缺點見 這篇文章 )。
沒有仔細看的同窗可能會問:我明明就是按照第一種方式寫的,也正常work沒問題呀。說你是不細心吧還真是,不信你再回去瞅瞅對比對比。若是你用第一種方式而且可以「正常work」,那請你查查類頭上是否是標註有 @Configuration 註解?
Full模式是容錯性最強的一種方式,你亂造都行,沒啥顧慮。
固然嘍,方法不能是private/final。但通常狀況下誰會在配置裏final掉一個方法呢?你說對吧~
@Configuration
public class AppConfig {
@Bean
public Son son() {
Son son = new Son();
System.out.println("son created..." + son.hashCode());
return son;
}
@Bean
public Parent parent() {
Son son = son();
System.out.println("parent created...持有的Son是:" + son.hashCode());
return new Parent(son);
}
}
複製代碼
運行程序,結果輸出:
son created...1797712197
parent created...持有的Son是:1797712197
com.yourbatman.fullliteconfig.config.AppConfig$$EnhancerBySpringCGLIB$$8ef51461@be64738
容器內的Son實例:1797712197
容器內Person持有的Son實例:1797712197
true
複製代碼
結果是完美的。它可以保證你經過調用標註有@Bean的方法獲得的是IoC容器裏面的實例對象,而非從新建立一個。相比較於Lite模式,它還有另一個區別:它會爲配置類生成一個 CGLIB 的代理子類對象放進容器,而Lite模式放進容器的是原生對象。
凡事皆有代價,一切皆在取捨。原生的纔是效率最高的,是對Cloud Native最爲友好的方式。但在實際「推薦使用」上, 業務端開發通常只會使用Full模式 ,畢竟業務開發的同窗水平是殘良莠不齊的,容錯性就顯得相當重要了。
若是你是容器開發者、中間件開發者...推薦使用Lite模式配置,爲容器化、Cloud Native作好準備嘛~
Full模式既然是面向使用側爲經常使用的方式,那麼接下來就趴一趴Spring究竟是施了什麼「魔法」,讓調用@Bean方法居然能夠不進入方法體內而指向同一個實例。
終於到了今天的主菜。關於前面的流程分析本文就一步跳過,單刀直入分析 BeanMethodInterceptor 這個攔截器,也也就是所謂的兩個攔截器的 後者 。
舒適提示:請務必確保已經瞭解過了上篇文章的流程分析哈,否則下面內容很容易形成你 腦力不適 的
相較於上個攔截器,這個攔截器不可爲不復雜。官方解釋它的做用爲:攔截任何標註有 @Bean註解的方法的 調用 ,以確保正確處理Bean語義,例如 做用域 (請別忽略它)和AOP代理。
複雜歸複雜,但沒啥好怕的,一步一步來唄。一樣的,我會按以下兩步去了解它: 執行時機+ 作了何事 。
廢話很少說,直接結合源碼解釋。
BeanMethodInterceptor:
@Override
public boolean isMatch(Method candidateMethod) {
return (candidateMethod.getDeclaringClass() != Object.class &&
!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}
複製代碼
三行代碼,三個條件:
簡而言之, **標註有@Bean註解方法 執行時 會被攔截 **。
因此下面例子中的son()和parent()這兩個, 以及parent()裏面調用的son()方法的執行 它都會攔截(一共攔截3次)~
小細節:方法只要是個Method便可, 不管是static方法 仍是普通方法,都會「參與」此判斷邏輯哦
這裏是具體攔截邏輯,會比第一個攔截器複雜不少。源碼不算很是的多,但牽扯到的東西還真很多,好比AOP、好比Scope、好比Bean的建立等等, 理解起來還蠻費勁的 。
本處以攔截到 parent() 方法的執行爲例,結合源碼進行跟蹤講解:
BeanMethodInterceptor:
// enhancedConfigInstance:被攔截的對象實例,也是代理對象
// beanMethod:parent()方法
// beanMethodArgs:空
// cglibMethodProxy:代理。用於調用其invoke/invokeSuper()來執行對應的方法
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance,
Method beanMethod, Object[] beanMethodArgs, MethodProxy cglibMethodProxy) throws Throwable {
// 經過反射,獲取到Bean工廠。也就是$$beanFactory這個屬性的值~
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
// 拿到Bean的名稱
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// 判斷這個方法是不是Scoped代理對象 很明顯本利裏是沒有標註的 暫先略過
// 簡答的說:parent()方法頭上是否標註有@Scoped註解~~~
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// ========下面要處理bean間方法引用的狀況了========
// 首先:檢查所請求的Bean是不是FactoryBean。也就是bean名稱爲`&parent`的Bean是否存在
// 若是是的話,就建立一個代理子類,攔截它的getObject()方法以返回容器裏的實例
// 這樣作保證了方法返回一個FactoryBean和@Bean的語義是效果同樣的,確保了不會重複建立多個Bean
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
// 先獲得這個工廠Bean
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
// Scoped proxy factory beans are a special case and should not be further proxied
// 若是工廠Bean已是一個Scope代理Bean,則不須要再加強
// 由於它已經可以知足FactoryBean延遲初始化Bean了~
}
// 繼續加強
else {
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
// 檢查給定的方法是否與當前調用的容器相對應工廠方法。
// 比較方法名稱和參數列表來肯定是不是同一個方法
// 怎麼理解這句話,參照下面詳解吧
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// 這是個小細節:若你@Bean返回的是BeanFactoryPostProcessor類型
// 請你使用static靜態方法,不然會打印這句日誌的~~~~
// 由於若是是非靜態方法,部分後置處理失效處理不到你,可能對你程序有影像
// 固然也可能沒影響,因此官方也只是建議而已~~~
if (logger.isInfoEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
... // 輸出info日誌
}
// 這表示:當前parent()方法,就是這個被攔截的方法,那就沒啥好說的
// 至關於在代理代理類裏執行了super(xxx);
// 可是,可是,可是,此時的this依舊是代理類
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// parent()方法裏調用的son()方法會交給這裏來執行
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
複製代碼
步驟總結:
這是該攔截器的執行步驟,留下兩個答:question:下面我來一一解釋(按照倒序)。
這是最爲常見的case。示例代碼:
@Configuration
public class AppConfig {
@Bean
public Son son() {
Son son = new Son();
System.out.println("son created..." + son.hashCode());
return son;
}
@Bean
public Parent parent() {
notBeanMethod();
Son son = son();
System.out.println("parent created...持有的Son是:" + son.hashCode());
return new Parent(son);
}
public void notBeanMethod(){
System.out.println("notBeanMethod invoked by 【" + this + "】");
}
}
複製代碼
本配置類一共有 三個 方法:
所以它最終交給 cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);方法直接執行父類(也就是目標類)的方法體:
值得注意的是: 此時所處的對象仍舊是代理對象內 ,這個方法體只是經過代理類調用了 super(xxx) 方法進來的而已嘛~
同上,會走到目標類的方法體裏,開始調用 notBeanMethod()和son() 這兩個方法,這個時候處理的方式就不同了:
BeanMethodInterceptor:
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {
// 當前bean(son這個Bean)是否正在建立中... 本處爲false嘛
// 這個判斷主要是爲了防止後面getBean報錯~~~
boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
try {
// 若是該Bean確實正在建立中,先把它標記下,放置後面getBean報錯~
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, false);
}
// 更具該方法的入參,決定後面使用getBean(beanName)仍是getBean(beanName,args)
// 基本原則是:但凡只要有一個入參爲null,就調用getBean(beanName)
boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
if (useArgs && beanFactory.isSingleton(beanName)) {
for (Object arg : beanMethodArgs) {
if (arg == null) {
useArgs = false;
break;
}
}
}
// 經過getBean從容器中拿到這個實例 本處拿出的就是Son實例嘍
Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) : beanFactory.getBean(beanName));
// 方法返回類型和Bean實際類型作個比較,由於有可能類型不同
// 何時會出現類型不同呢?當BeanDefinition定義信息類型被覆蓋的時候,就可能出現此現象
if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
if (beanInstance.equals(null)) {
beanInstance = null;
} else {
...
throw new IllegalStateException(msg);
}
}
// 當前被調用的方法,是parent()方法
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
if (currentlyInvoked != null) {
String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
// 這一步是註冊依賴關係,告訴容器:
// parent實例的初始化依賴於son實例
beanFactory.registerDependentBean(beanName, outerBeanName);
}
// 返回實例
return beanInstance;
}
// 歸還標記:筆記實際確實還在建立中嘛~~~~
finally {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, true);
}
}
}
複製代碼
這麼一來,執行完parent()方法體裏的son()方法後, 實際獲得的是容器內的實例 ,從而保證了咱們這麼寫是不會有問題的。
以上程序的運行結果是:
son created...347978868
notBeanMethod invoked by 【com.yourbatman.fullliteconfig.config.AppConfig$$EnhancerBySpringCGLIB$$ec611337@12591ac8】
parent created...持有的Son是:347978868
com.yourbatman.fullliteconfig.config.AppConfig$$EnhancerBySpringCGLIB$$ec611337@12591ac8
容器內的Son實例:347978868
容器內Person持有的Son實例:347978868
true
複製代碼
能夠看到, Son自始至終都只存在一個實例 ,這是符合咱們的預期的。
一樣的代碼,在Lite模式下(去掉@Configuration註解便可),不存在「如此複雜」的代理邏輯,因此上例的運行結果是:
son created...624271064
notBeanMethod invoked by 【com.yourbatman.fullliteconfig.config.AppConfig@21a947fe】
son created...90205195
parent created...持有的Son是:90205195
com.yourbatman.fullliteconfig.config.AppConfig@21a947fe
容器內的Son實例:624271064
容器內Person持有的Son實例:90205195
false
複製代碼
這個結果很好理解,這裏我就再也不囉嗦了。總之就不能這麼用就對了~
FactoryBean 也是向容器提供Bean的一種方式,如最多見的 SqlSessionFactoryBean 就是這麼一個大表明,由於它比較經常使用,而且這裏也做爲此攔截器一個 單獨的執行分支 ,因此頗有必要研究一番。
執行此分支邏輯的條件是:容器內已經存在 &beanName 和 beanName 兩個Bean。執行的方式是:使用 enhanceFactoryBean() 方法對 FactoryBean 進行加強。
ConfigurationClassEnhancer:
// 建立一個子類代理,攔截對getObject()的調用,委託給當前的BeanFactory
// 而不是建立一個新的實例。這些代理僅在調用FactoryBean時建立
// factoryBean:從容器內拿出來的那個已經存在的工廠Bean實例(是工廠Bean實例)
// exposedType:@Bean標註的方法的返回值類型
private Object enhanceFactoryBean(Object factoryBean, Class<?> exposedType,
ConfigurableBeanFactory beanFactory, String beanName) {
try {
// 看看Spring容器內已經存在的這個工廠Bean的狀況,看看是否有final
Class<?> clazz = factoryBean.getClass();
boolean finalClass = Modifier.isFinal(clazz.getModifiers());
boolean finalMethod = Modifier.isFinal(clazz.getMethod("getObject").getModifiers());
// 類和方法其中有一個是final,那就只能看看能不能走接口代理嘍
if (finalClass || finalMethod) {
// @Bean標註的方法返回值如果接口類型 嘗試走基於接口的JDK動態代理
if (exposedType.isInterface()) {
// 基於JDK的動態代理
return createInterfaceProxyForFactoryBean(factoryBean, exposedType, beanFactory, beanName);
} else {
// 類或方法存在final狀況,可是呢返回類型又不是
return factoryBean;
}
}
}
catch (NoSuchMethodException ex) {
// 沒有getObject()方法 很明顯,通常不會走到這裏
}
// 到這,說明以上條件不知足:存在final且還不是接口類型
// 類和方法都不是final,生成一個CGLIB的動態代理
return createCglibProxyForFactoryBean(factoryBean, beanFactory, beanName);
}
複製代碼
步驟總結:
說明:不管是JDK動態代理仍是CGLIB的代理實現均很是簡單,就是把getObject()方法代理爲使用 beanFactory.getBean(beanName) 去獲取實例(要不代理掉的話,每次不就執行你getObject()裏面的邏輯了麼,就又會建立新實例啦~)
須要明確,此攔截器對FactoryBean邏輯處理分支的目的是:確保你 經過方法調用 拿到 FactoryBean 後,再調用其 getObject() 方法(哪怕調用屢次)獲得的都是同一個示例(容器內的單例)。所以須要對 getObject() 方法作攔截嘛,讓該方法指向到 getBean() ,永遠從容器裏面拿便可。
這個攔截處理邏輯只有在@Bean方法調用時纔有意義,好比parent()裏調用了son()這樣子纔會起到做用,不然你就忽略它吧~
針對於此,下面給出不一樣case下的代碼示例,增強理解。
準備一個 SonFactoryBean 用於產生Son實例:
public class SonFactoryBean implements FactoryBean<Son> {
@Override
public Son getObject() throws Exception {
return new Son();
}
@Override
public Class<?> getObjectType() {
return Son.class;
}
}
複製代碼
而且在配置類裏把它放好:
@Configuration
public class AppConfig {
@Bean
public FactoryBean<Son> son() {
SonFactoryBean sonFactoryBean = new SonFactoryBean();
System.out.println("我使用@Bean定義sonFactoryBean:" + sonFactoryBean.hashCode());
System.out.println("我使用@Bean定義sonFactoryBean identityHashCode:" + System.identityHashCode(sonFactoryBean));
return sonFactoryBean;
}
@Bean
public Parent parent(Son son) throws Exception {
// 根據前面所學,sonFactoryBean確定是去容器拿
FactoryBean<Son> sonFactoryBean = son();
System.out.println("parent流程使用的sonFactoryBean:" + sonFactoryBean.hashCode());
System.out.println("parent流程使用的sonFactoryBean identityHashCode:" + System.identityHashCode(sonFactoryBean));
System.out.println("parent流程使用的sonFactoryBean:" + sonFactoryBean.getClass());
// 雖然sonFactoryBean是從容器拿的,可是getObject()你可不能保證每次都返回單例哦~
Son sonFromFactory1 = sonFactoryBean.getObject();
Son sonFromFactory2 = sonFactoryBean.getObject();
System.out.println("parent流程使用的sonFromFactory1:" + sonFromFactory1.hashCode());
System.out.println("parent流程使用的sonFromFactory1:" + sonFromFactory2.hashCode());
System.out.println("parent流程使用的son和容器內的son是否相等:" + (son == sonFromFactory1));
return new Parent(sonFromFactory1);
}
}
複製代碼
運行程序:
@Bean
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
SonFactoryBean sonFactoryBean = context.getBean("&son", SonFactoryBean.class);
System.out.println("Spring容器內的SonFactoryBean:" + sonFactoryBean.hashCode());
System.out.println("Spring容器內的SonFactoryBean:" + System.identityHashCode(sonFactoryBean));
System.out.println("Spring容器內的SonFactoryBean:" + sonFactoryBean.getClass());
System.out.println("Spring容器內的Son:" + context.getBean("son").hashCode());
}
複製代碼
輸出結果:
我使用@Bean定義sonFactoryBean:313540687
我使用@Bean定義sonFactoryBean identityHashCode:313540687
parent流程使用的sonFactoryBean:313540687
parent流程使用的sonFactoryBean identityHashCode:70807318
parent流程使用的sonFactoryBean:class com.yourbatman.fullliteconfig.config.SonFactoryBean$$EnhancerBySpringCGLIB$$1ccec41d
parent流程使用的sonFromFactory1:910091170
parent流程使用的sonFromFactory1:910091170
parent流程使用的son和容器內的son是否相等:true
Spring容器內的SonFactoryBean:313540687
Spring容器內的SonFactoryBean:313540687
Spring容器內的SonFactoryBean:class com.yourbatman.fullliteconfig.config.SonFactoryBean
Spring容器內的Son:910091170
複製代碼
結果分析:
達到了預期的效果:parent在調用son()方法時,獲得的是在容器內已經存在的 SonFactoryBean基礎上CGLIB字節碼 提高過的實例 , 攔截成功 ,從而getObject()也就實際是去容器裏拿對象的。
經過本例有以下小細節須要指出:
public final class SonFactoryBean implements FactoryBean<Son> { ... }
複製代碼
再次運行程序,結果輸出爲:執行的結果同樣,只是代理方式不同而已。從這個小細節你也能看出來Spring對代理實現上的偏向: 優先選擇CGLIB代理方式,JDK動態代理方式用於兜底 。
...
// 使用了JDK的動態代理
parent流程使用的sonFactoryBean:class com.sun.proxy.$Proxy11
...
複製代碼
提示:若你標註了final關鍵字了,那麼請保證@Bean方法返回的是 FactoryBean 接口,而不能是 SonFactoryBean 實現類,不然最終沒法代理了,原樣輸出。由於JDK動態代理和CGLIB都搞不定了嘛~
在以上例子的基礎上,我給它「加點料」,再看看效果呢:
使用 BeanDefinitionRegistryPostProcessor 提早就放進去一個名爲son的實例:
// 這兩種方式向容器扔bd or singleton bean都行 我就選擇第二種嘍
// 注意:此處放進去的是BeanFactory工廠,名稱是son哦~~~ 不要寫成了&son
@Component
public class SonBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// registry.registerBeanDefinition("son", BeanDefinitionBuilder.rootBeanDefinition(SonFactoryBean.class).getBeanDefinition());
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
SonFactoryBean sonFactoryBean = new SonFactoryBean();
System.out.println("初始化時,註冊進容器的sonFactoryBean:" + sonFactoryBean);
beanFactory.registerSingleton("son", sonFactoryBean);
}
}
複製代碼
再次運行程序,輸出結果:
初始化時最先進容器的sonFactoryBean:2027775614
初始化時最先進容器的sonFactoryBean identityHashCode:2027775614
parent流程使用的sonFactoryBean:2027775614
parent流程使用的sonFactoryBean identityHashCode:1183888521
parent流程使用的sonFactoryBean:class com.yourbatman.fullliteconfig.config.SonFactoryBean$$EnhancerBySpringCGLIB$$1ccec41d
parent流程使用的sonFromFactory1:2041605291
parent流程使用的sonFromFactory1:2041605291
parent流程使用的son和容器內的son是否相等:true
Spring容器內的SonFactoryBean:2027775614
Spring容器內的SonFactoryBean:2027775614
Spring容器內的SonFactoryBean:class com.yourbatman.fullliteconfig.config.SonFactoryBean
Spring容器內的Son:2041605291
複製代碼
效果上並不差別,從日誌上能夠看到:你配置類上使用@Bean標註的son() 方法體 並沒執行了,而是使用的最開始註冊進去的實例,差別僅此而已。
爲什麼是這樣的現象?這就不屬於本文的內容了,是Spring容器對Bean的實例化、初始化邏輯,本公衆號後面依舊會採用專欄式講解,讓你完全弄懂它。當前有興趣的能夠先自行參考 DefaultListableBeanFactory#preInstantiateSingletons 的內容~
Lite模式下可沒這些「增強特性」,因此在Lite模式下(拿掉 @Configuration 這個註解即可)運行以上程序,結果輸出爲:
我使用@Bean定義sonFactoryBean:477289012
我使用@Bean定義sonFactoryBean identityHashCode:477289012
我使用@Bean定義sonFactoryBean:2008966511
我使用@Bean定義sonFactoryBean identityHashCode:2008966511
parent流程使用的sonFactoryBean:2008966511
parent流程使用的sonFactoryBean identityHashCode:2008966511
parent流程使用的sonFactoryBean:class com.yourbatman.fullliteconfig.config.SonFactoryBean
parent流程使用的sonFromFactory1:433874882
parent流程使用的sonFromFactory1:572191680
parent流程使用的son和容器內的son是否相等:false
Spring容器內的SonFactoryBean:477289012
Spring容器內的SonFactoryBean:477289012
Spring容器內的SonFactoryBean:class com.yourbatman.fullliteconfig.config.SonFactoryBean
Spring容器內的Son:211968962
複製代碼
結果解釋我就再也不囉嗦,有了前面的基礎就太容易理解了。
要解釋好這個緣由,和 @Scope 代理方式的原理知識強相關。限於篇幅,本文就先賣個關子~
關於 @Scope 我我的以爲足夠用5篇以上文章專題講解,雖然在 Spring Framework 裏使用得比較少,可是在理解 Spirng Cloud 的自定義擴展實現上顯得很是很是有必要,因此你可關注我公衆號,會近期推出相關專欄的。
關於Spring配置類這個專欄內容,講解到這就完成99%了,絕不客氣的說關於此部分知識真正能夠實現「橫掃千軍」,據我瞭解沒有解決不了的問題了。