invokeBeanFactoryPostProcessors(上)
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//這里的參數getBeanFactoryPostProcessors()講道理很容易誤導大家,我在調試的時候發現它什么都沒返回,注意他們的區別,list和map
//回到之前我所以提到的實現了BeanFactoryPostProcessor的Config類,按道理應該是有的,但是其實它拿到的是一個list,
//而我們是放在map里面的,所以這里它只是拿到程序員自己手動add的,注意注解方式也是拿不到的
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
看這里的參數getBeanFactoryPostProcessors()講道理很容易誤導人,我在調試的時候發現它什么都沒返回,注意他們的區別,,這里會有個坑,如果讀者嘗試在我們的Main函數加上這段代碼:
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext=
new AnnotationConfigApplicationContext(AppConfig.class);
annotationConfigApplicationContext.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
});
}
然后繼續調試getBeanFactoryPostProcessors(),得出的結果會是多少呢,其實還是0,至于為什么,我們換一種寫法
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.regisrer(Appconfig.class);
annotationConfigApplicationContext.addBeanFactoryPostProcessor();
annotationConfigApplicationContext.refresh();
再次調試getBeanFactoryPostProcessors(),得出結果為1,相信讀者應該有多感悟。搞明白這個之后我們繼續進入invokeBeanFactoryPostProcessors
這個方法,
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
//注意這里的beanFactory是最基本的子類,這個類包含了如何對bean注冊,創建,維護等功能,所以一定會進去
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
//這里定義兩個list是為了區分子類是實現了BeanFactoryPostProcessor,而且這里是裝的程序員自己添加的
// 還是BeanDefinitionRegistryPostProcessor,因為這兩個接口是父子關系,所以功能有所區別
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
//這里肯定為空,前面已經說了,我們這里是程序員自己add的,而我們并沒有
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
}
else {
regularPostProcessors.add(postProcessor);
}
}
首先遍歷beanFactoryPostProcessors,注意這個參數是我們傳進來,是從getBeanFactoryPostProcessors()獲取的,因此一般情況我們不會手動設置,所以這里都會為空,我們往下獲取這個ConfigurationClassPostProcessor類后,Spring會合并之前兩個循環的后置處理器,然后到主干方法invokeBeanDefinitionRegistryPostProcessors
我們在跟蹤進去:
看第二句代碼String[] candidateNames = registry.getBeanDefinitionNames();這里會拿出6個Name,添加到configCandidates里,看調試結果回想我在前文所提出的注冊的后置處理器就明白為什么是6個,其目的就是為了找出我們的配置類,如果找到就標記成full,有些讀者可能了解Spring的@configuration注解是配置類的意思,但不知道讀者是否了解過加和不加這個注解的作用是什么,這里我們把@configuration注解注釋掉,嘗試運行會發現結果并不會報錯,
至于為什么Spring搞這么個注解,筆者會在后文對@configuration的作用以及源碼實現進行詳細介紹,我們繼續回到代碼:
這里我們就拿到了我們的配置類,既然拿到了配置類,那么下一步就對配置類進行解析,看代碼:
看到這句代碼 parser.parse();這句代碼是Spring解析的核心代碼,我們點進去
這里判斷需要解析的BeanDefinition是那種類型,這里回到我們的入口
,我們注冊的是AppConfig配置類,但其實我們還可以配置普通類,比如我們直接將我們的User類放進去
隨后我們在調試parse->processConfigurationClass->
-> doProcessConfigurationClass(configClass, sourceClass);
看第一段代碼,如果我們的配置類有標識了@Component 這樣的類
,可以看到已經找到這么一個類,這段代碼我們點到為止,因為下面的配置類信息解析包含了這里的設計思想,所以這里不重點分析。
首先取出我們的掃描注解
然后執行 Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan,
sourceClass.getMetadata().getClassName());這段代碼,這里就是解析的核心代碼,我們跟進去,
我們進入doscan方法
這里我們看到在經過了findCandidateComponents(basePackage);這個方法后,Spring就已經拿到我了我們定義的注解類,我們繼續進入
可以看出Spring真正解決包掃描結束是采用的ASM字節碼提取技術,筆者對此也是一知半解,所以就不多做介紹了,有興趣的讀者們可以自行深入研究,這里我就不做文章了,我們還是回到代碼,下面的代碼不太重要,因此Spring拿到掃描出來的類后就返回,好的我們繼續回到代碼
在對普通注解類解析完后,會將普通類拿出來作為當做配置類繼續遞歸解析,當然通常都會跳過,然后開始對@import注解解析,有些讀者可能還沒見過這個@import類,我們看下@Import注解類
,它的作用很清晰,目的就是向Spring容器注冊類,看下實例:
這樣就和在MyImportBeanDefinitionRegistrar加上@Component是一樣效果,這里源碼的細節我帶著大家細看了,其目的就是解析@import三種類:普通類,ImportSelector,ImportBeanDefinitionRegistrar。這里我將筆記注釋貼出來,大家自行理解:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
//這里是處理ImportSelector,轉換成bd,放入一個集合
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
//這里是否是延遲執行,如果目標對象實現的是DeferredImportSelector,就表示需要延遲
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
//這里是遞歸,因為Import的類可能還有Import,如果沒有會遞歸到下面的普通類放入集合
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
//ImportBeanDefinitionRegistrar這個是Import的第二種類,實現可以直接拿到注冊器
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
//將實現了ImportBeanDefinitionRegistrar的子類放進importBeanDefinitionRegistrars,后面spring會對其處理
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
//普通import類和ImportSelector都放進這個configurationClasses
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
讀者這里重點對ImportBeanDefinitionRegistrar的作用進行分享,看下面實例:
如果我們繼承了ImportBeanDefinitionRegistrar,那么我們就可以拿到BeanDefinitionRegistry,并就可以手動的向Spring添加一個 BeanDefinition,我們調試一下
當前我們的BeanDefinitionMap是11個,等到我們執行完這段代碼后就會變成12個,看下圖:
,我們改變了BeanDefinitionMap,而BeanDefinitionMap在Spring創建對象起到了絕對作用,所以我們就改變了Bean的創建,而這就是擴展Spring的一個接口之一,后期我們有機會的話我們會重點分享Spring-Mybatis和Spring-AOP是如何利用這些接口拓展的。
在對@Import注解解析完之后,又開始對配置類的@Bean解析
,然后我們繼續回到代碼,在執行完parse方法后
然后做校驗,重點從這里開始->this.reader.loadBeanDefinitions(configClasses);,我們一直更進去會知道
這里就是對我們之前存儲的@Import注解類進行運行,并且會執行自定義的@Import類,到目前為止我們總算跑完了
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);這句代碼
接來下的內容筆者打算分開描述。