?invokeBeanFactoryPostProcessors(下)
在上一小節(jié)我們分析完了invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);這個方法干了那些事情,現(xiàn)在我們繼續(xù)對后文做分析昏兆,如圖:上圖Spring進行循環(huán)蕴茴,判斷是否有遺漏掉的后置處理器沒有解析谬哀,一般情況下,currentRegistryProcessors會是0奶卓,我們運行代碼都會直接跳過晴音,繼續(xù)往下走:
這里是Spring最后一次對BeanDefinitionRegistryPostProcessor后置處理器進行解析肘交,主要是針對一些父類層級關系玻孟,通常也為0唆缴,所以依舊不會有什么作用。好了黍翎,接下來到了關鍵代碼了
我們對invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);進行跟蹤->postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 面徽,我們將代碼停在下圖:
注意AppConfig.class的變化
這里它還是一個普通的類,我們調(diào)試下一步
可以發(fā)現(xiàn)匣掸,Spring將其增強成為了cglib增強類趟紊,那么AppConfig為什么會增強呢?我們下面進行詳細分析
@Configuration
當我們將 @Configuration取消時旺聚,
我們調(diào)試停在上面的代碼
可以看到這里的配置類并沒有被增強织阳,所以可以得出結論,我們的@Configuration可以將我們的普通類進行增強砰粹,不知讀者是否還記得我們在上一章所講full嗎,如果是 @Configuration類唧躲,Spring會認為這是一個配置類就會將其標識full,而這個full就是代表配置類是否需要增強碱璃,后面我們會在源碼中進行證明弄痹,好了,說了這么多嵌器,相信讀者應該對 @Configuration
有一定的了解肛真,現(xiàn)在我們來探討為什么Spring要增強 配置類,這里我們對加上@Configuration和取消@Configuration進行測試爽航,我們看下面代碼:
第一種情況-》取消@Configuration
第二種情況-》加上@Configuration
我們可以看到有了@Configuration蚓让,我們的user1()和user2()方法返回的對象是同一個對象,當然這里還有第三種情況加上static讥珍,讀者可以自行測試看看又是什么情況历极,結果會發(fā)現(xiàn)代理功能會失效,至于為什么會失效我們在后面的源碼會詳細說明衷佃。好了我們大概知道了@Configuration的作用趟卸,那么Spring是如何對@Configuration進行增強的呢,我們回到源碼中:
進入enhanceConfigurationClasses
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
?
//找到config注解類氏义,這里就可以看到full和lite了锄列,如果full會變成增強類,目的是為了實現(xiàn)單例
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
?
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
try {
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
if (configClass != null) {
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
}
catch (Throwable ex) {
throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
這里(ConfigurationClassUtils.isFullConfigurationClass(beanDef))會判斷有沒有標識了FULL的配置類惯悠,如果沒有下面configBeanDefs
會為空直接返回邻邮,如果不為空,對配置類增強吮螺,我們鎖定這段代碼Class<?> enhancedClass = enhancer.enhance(configClass,
this.beanClassLoader);-》Class<?> enhancedClass createClass(newEnhancer(configClass,classLoader));-》
newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader)最后來到了這里:
在對@Configuration做詳細分析時饶囚,筆者可以先自行思考帕翻,Spring對配置類進行增強之后如何做到采用不同的方式去創(chuàng)建對象,為什么在Spring的容器里都只會存放一份Bean萝风,如果要實現(xiàn)這種功能嘀掸,是不是需要改變創(chuàng)建對象的方式呢?那么既然我們的工廠我們的@Bean.class是創(chuàng)建對象交給Spring管理规惰,那么如果我們創(chuàng)建對象的方式先通過工廠里面獲取睬塌,如果獲取不到我們再創(chuàng)建對象交給Spring管理是不是就能夠?qū)崿F(xiàn)呢?好了歇万,我想讀者應該有些思路了揩晴,我們再看一張圖
圖上大致講解了配置類轉變?yōu)樵鰪婎惡蟠蟾攀鞘裁礃幼拥模瑥膱D上我們可以看到增強代理類有個私有變量贪磺,這個變量存放著我們的工廠追他,那么為什么需要這個工廠我想讀者應該有所頓悟犀农。好了我們再回到源碼:
如圖我們可以大概才到,Spring在這里添加了一個策略膏斤,這個策略是聲明一個變量名為"$$beanFactory"的公共屬性大渤,再回想我們之前添加的接口是不是可以得到工廠,那么我么你現(xiàn)在還差一步掸绞,如何將前面的原料加工就是我們代理需要去做的事情:
這個Callback筆者就不分析了,因為涉及到CGLIB的知識耕捞,如果讀者有情趣可以自行研究衔掸。好了我們生成代理后還差一件事情,那就是如何將我們拿到的工廠賦值給代理類得變量中俺抽,看下面代碼
我們點進去
這里讀者是不是豁然開朗敞映,是不是覺得Spring的設計如神一般的存在啊,好了當我們了解到這里其實
invokeBeanFactoryPostProcessors這個方法也差不多結束了磷斧,當然還有一些更細節(jié)的地方筆者會作為單獨的章節(jié)進行描述振愿,哎捷犹,總算是到了一半了,什么才一半冕末?萍歉,畢竟我們真正的對象實例還沒開始呢,好的我們在下一小節(jié)會對Spring剩余部分進行深度分析档桃。