深入理解 Spring BeanFactoryPostProcessor的回調


程序入口:

接着上一篇博客中看完了在AnnotationConfigApplicationContext的構造函數中的register(annotatedClasses);將我們傳遞進來的主配置類添加進了BeanFactory, 本片博客繼續跟進refresh(); 看看Spring如何繼續初始化Spring的環境

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
	this();
	register(annotatedClasses)
	refresh();
}

跟進refresh(), 源碼如下: 主要做了如下幾件工作

  • 刷新的預准備
    • 比如: 設置時間的錨點,加載上下文環境變量
  • 獲取BeanFactory
  • 執行所有的BeanFactoryPostProcessor
  • 執行所有的BeanPostProcessor
  • ...
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
	// Prepare this context for refreshing.
	//准備刷新
	prepareRefresh();

    //獲取BeanFactory 
	ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // 准備BeanFactory
	prepareBeanFactory(beanFactory);
	try {
		// 方法中沒有任何實現的邏輯
		postProcessBeanFactory(beanFactory);

		// invoke BeanFactoryPostprocessor, 執行bean工廠的后置處理器
		//如果我們沒有手動往Spring中注入bean工廠的后置處理器,那么此時僅有一個,也是beanFactoryMap中的第一個RootBeanDefinition-> ConfigurationClassPostProcessor
		invokeBeanFactoryPostProcessors(beanFactory);

		// 注冊 bean的后置處理器, 這些處理器可以在bean的構造方法執行之后再執行init()方法前后執行指定的邏輯
		registerBeanPostProcessors(beanFactory);

		// Initialize message source for this context.
		initMessageSource();

		// Initialize event multicaster for this context.
		initApplicationEventMulticaster();

		// Initialize other special beans in specific context subclasses.
		onRefresh();

		// Check for listener beans and register them.
		registerListeners();

		// Instantiate all remaining (non-lazy-init) singletons.
		finishBeanFactoryInitialization(beanFactory);

		// Last step: publish corresponding event.
		finishRefresh();
	}
  }
}

刷新的准備工作

這個方法沒啥可看的重要邏輯,記錄了下開始的時間,然后為Spring的上下文加載可用的環境變量

	protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();
		this.closed.set(false);
		this.active.set(true);

		if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}

		//  這個是protected類型的方法,目前還沒有任何實現
		initPropertySources();

		//   校驗所有需要的properties是否都被解析過了
		//   getEnvironment() 得到系統環境, 后續的@Profile使用
		getEnvironment().validateRequiredProperties();
		
		this.earlyApplicationEvents = new LinkedHashSet<>();
	}

獲取BeanFactory

獲取出BeanFactory,接下來的工作重點是去掃描出程序員提供的類,然后將它們放進BeanFactoryMap中,在此過程中穿插執行BeanFactoryPostProcessorBeanPostPorcessor, 不難看出后續工作的進展都離不開這個BeanFactoryMap,這個map在哪里呢? 就在我們的beanFactory中,因此在刷新的最開始,獲取出bean工廠

繼承類圖

當前類是AbstractApplicationContext,上圖是它的繼承類圖,通過上圖可以看到,它是入口AnnotationConfigApplicationContextGenericApplicationContext的父類,而Spring的BeanFactory是在GenericApplicationContext中實例化的,故, 獲取beanFactory的邏輯肯定在當前方法中被設計成抽象的方法,而由自己具體實現,源碼如下:

	@Override
	public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

准備beanFactory

上面的邏輯是獲取出BeanFactory, 那什么是准備BeanFactory呢? 看它的注解解釋是: 為BeanFactory配置上它應該具有的所有特征, 那BeanFactory應該有什么特征呢? 類加載器 , bean表達式的解析器 , property與對象的轉換器 , bean的后置處理器 , 添加禁止用戶注入的bean的信息 , 注入bean的替換 , 添加默認的和環境相關的bean

源碼如下:它的解析我們寫在下面

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	// 添加類加載器
	beanFactory.setBeanClassLoader(getClassLoader());

	// 設置bean標簽的解析器, 一般我們使用spel標簽比較多,但是Spring也有自己的Bean標簽 
	beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

	// </property ref="XXX"> 解析轉換xxx 替換成對象
	beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

	// 添加bean的后置處理器,很顯然這里添加的是Spring自己的后置處理器
	beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

	// 當用戶企圖注入下面類型的對象時, 會被Spring忽略
	beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
	beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
	beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
	beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
	beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

	//用戶穿進來的是 BeanFactory.class , 那Spring會將她替換成beanFactory
	beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
	beanFactory.registerResolvableDependency(ResourceLoader.class, this);
	beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
	beanFactory.registerResolvableDependency(ApplicationContext.class, this);

	// Register early post-processor for detecting inner beans as ApplicationListeners.
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

	// Detect a LoadTimeWeaver and prepare for weaving, if found.
	if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
		// Set a temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
	}

	// 意思是如果自定義的Bean中沒有名為"systemProperties"和"systemEnvironment"的Bean,
	// 則注冊兩個Bena,Key為"systemProperties"和"systemEnvironment",Value為Map,
	// 這兩個Bean就是一些系統配置和系統環境信息
	//  注冊默認的和環境相關的 bean   Sping會檢測,我們自己注冊進來的Bean中有沒有下面的名字叫下面三個串的對象, 沒有的話就幫我們注入進來
	if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) { //   environment_bean_name
		beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {//   system_properties_bean_name
		beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
	}
	if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {//   system_environment_bean_name
		beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
	}
}

如上代碼的作用就是為BeanFactory初始化了Spring規定的幾個必須的配置屬性,其中比較值得注意的地方就是它添加的BeanPostProcessor, 雖然這是Spring原生的bean的后置處理器,但是也是第一次出現,很有意義,配置expressContext等工作, 這個后置處理器會在Bean的構造的構造過程中,動態的攔截插手

此外,添加了忽略注入的對象,當程序員向注入Spring啟動時,依賴的原生對象時,會被忽略注入,企圖注入BeanFactory,資源解析器,事件發布器,應用上下文時,被Spring使用原生的對象替換掉

執行所有的BeanFactory的后置處理器

執行BeanFactory的后置處理器,具體是哪些呢? 其實是兩部分,一部分是用戶自己添加的,另一部分是Spring在啟動過程中自己添加進去的

先說用戶自己添加的情況,不知道大家有沒有發現,源碼讀到這里其實還沒看到Spring進行包掃描,既然沒有進行包掃描那程序員通過@Compoennt注解添加進去的 bean工廠的后置處理器 就還沒有被Spring所識別到,沒錯,這里能被識別到的 BeanFactoryPostProcessor是程序員通過context.addBeanFactoryPostProcessor(new MyBeanFactoryPostProcessor());添加進來的

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {

	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()));
	}
}

繼續跟進invokeBeanFactoryPostProcessors()方法,源碼如下,這個方法在執行BeanFactoryPostProcessor不得不說他真的很重要,很長

它的主要邏輯是: 開始是一個if分支判斷beanFactory的合法性,上篇博文中我們看到了,所謂的beanFactory其實本身也實現了注冊器接口,有注冊bean的功能, 於是他將BeanFactory強轉成了注冊器類型

緊接着Spring定義了兩個新的List, 一個叫regularPostProcessors一個叫registryProcessors, 前者用來存放程序員自己添加進來的BeanFactoryPostProcessor, 后者用來存放程序員自己添加進來的BeanDefinitionRegistryPostProcessor, 為什么使用兩個集合呢? 參見下圖:

beanPostProcessor繼承體系圖

通過上面的圖可以看到,BeanFactoryPostProcessor是頂級的接口,BeanDefinitionRegistryPostProcessor是繼承了頂級接口然后自己做出了拓展, 一般程序員通過BeanFactoryPostProcessor在bean的構造方法之前進行插手的話,最常用的就是選擇直接自己實現BeanFactoryPostProcessor,雖然實現BeanDefinitionRegistryPostProcessor也行

BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor做出來拓展, Spring需要保證拓展的方法被執行到,重寫的父類的方法也要被執行到,因此選擇使用兩個集合,循環所有的bean工廠的后置處理器,按照不同的分類分別對待

分好類之后,又創建了一個list叫currentRegistryProcessors這個List中存放的是 Spring自己的提供的BeanFactoryPostProcessor的實現, 其實這個實現在前面提到過好多次了. 他就是ConfigurationClassPostProcessor

現在一共是三個集合,其中兩個集合中的存放的對象是一樣的,於是Spring將其實兩個存放BeanDefinitionRegistryPostProcessor的集合進行了合並

接下來就是真正的開始執行的工作

  • 執行BeanDefinitionRegistryPostProcessor
  • 執行BeanFactoryPostProcessor
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();

//   通過看BanFactory的繼承體系,能看到它實現了 BeanDefinitionRegistry接口
if (beanFactory instanceof BeanDefinitionRegistry) {
//    將工廠強轉成 注冊器
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

//   存放程序員添加進來的 BeanFactoryPostProcessor
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
//   存放程序員添加進來的 BeanDefinitionRegistryPostProcessor
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

System.out.println(beanFactoryPostProcessors.size());

//  自定義的beanFactoryPostProcessors
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {

	if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
		BeanDefinitionRegistryPostProcessor registryProcessor =
				(BeanDefinitionRegistryPostProcessor) postProcessor;
		registryProcessor.postProcessBeanDefinitionRegistry(registry);
		registryProcessors.add(registryProcessor);
	}
	else {//BeanDefinitionRegistryPostProcessor  BeanfactoryPostProcessor
		//   將自定義的BeanFactoryPostPorcessor 添加到了 上面的 ArrayList中regularPostProcessors
		regularPostProcessors.add(postProcessor);
	}
}


List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

String[] postProcessorNames =
		beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

for (String ppName : postProcessorNames) {
	if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
		currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
		processedBeans.add(ppName);
	}
}

sortPostProcessors(currentRegistryProcessors, beanFactory);

registryProcessors.addAll(currentRegistryProcessors);

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

currentRegistryProcessors.clear();

postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
	if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
		currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
		processedBeans.add(ppName);
	}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
boolean reiterate = true;
while (reiterate) {
	reiterate = false;
	postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
	for (String ppName : postProcessorNames) {
		if (!processedBeans.contains(ppName)) {
			currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
			processedBeans.add(ppName);
			reiterate = true;
		}
	}
	sortPostProcessors(currentRegistryProcessors, beanFactory);
	registryProcessors.addAll(currentRegistryProcessors);
	invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
	currentRegistryProcessors.clear();
}


invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);


invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
...
}

接下來與其說 看一下它如何執行BeanDefinitionRegistryPostProcessor, 到不如說 看一下如何執行ConfigrationClassPostProcessorBeanFactoryPostProcessor的拓展方法

postProcessor.postProcessBeanDefinitionRegistry(registry);

繼續跟進,經過兩個沒有啥重要邏輯的方法之后,進入到下面的邏輯中, 有來了一個高潮,在這個方法中完成了包掃描工作

一開始獲取出beanFactoryMap中的全部的BeanDefinitionName, 這時一共有幾個? 其實是7個, 其中6個是在創建AnnotatedBeanDefinitionReader時添加進去的6個, 另外的哪一個就是在前面的register()方法中,注冊的我們的主配置類MainConfig

緊接着是一個循環判斷語句,循環這七個BeanDefinition, 目的有兩個,第一個把我們自己的MainConfig配置類找出來放到下面的configCandidates集合中,因為這是個配置類啊,上面會有@ComponentScan(value="XXX") 通過這個注解提供的包信息,Spring就能進一步進行包掃描,找到用戶提供的所有的類信息,將它們加載進容器中, 第二個判斷一下我們的MainConfig類上有沒有添加@Configuration 如果存在這個注解標記它為full,Spring就認為我們的當前的運行的上下文環境是全注解環境,並且會為MainConfig生成一個cglib代理對象,進一步保證了Spring的單例特征,如果沒有這個注解,但是存在@Component @ComponentScan @Import @ImportResourceSpring標記它為lite.認為當前的上下文環境為非全注解模式

怎么理解這個全注解與非全注解呢? 字面意思也是,全注解就是不存在配置文件, 不存在配置文件的話,程序員不可能不提供@ComponentScan讓Spring去掃描完成Bean的注入,同時程序員也會提供一個@Configuration明確的標識這是一個配置類, 那非全注解呢, 就是可能存在注解和XML共存的現象, Spring這時也會同時支持注解+xml的讀取

接着又是排序,創建名稱生成器

緊接着創建了一個ConfigurationClassParser配置類的解析器,這個解析器,見名知意,用來解析配置類, 現在誰是配置類呢? 其實就是在上面的循環中唯一被添加進list的,我們提供的MainConfig, 因為我在他身上添加了@Configuration注解

下面的主要邏輯是解析配置類,我把解釋寫在如下代碼的下面

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//  定義一個List 存放項目中添加了 @Compennt注解的類
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();

//  獲取容器中注冊的所有bd名字
//  一共 7個 , 6個rootBeanDefinition  1個我們自己的MainConfig
//   獲取出一開始我們Spring自己添加的6個Processor, 和我們添加的MainConfig
String[] candidateNames = registry.getBeanDefinitionNames();

for (String beanName : candidateNames) {
	BeanDefinition beanDef = registry.getBeanDefinition(beanName);
	//  
	if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
			ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
		if (logger.isDebugEnabled()) {
			logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
		}
	}

	else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {

		configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
	}
}

//   他是怎么判斷出來的呢? 在上面, 如果判斷得出當前的類添加了@Configration , 就給他標記 full, 在上面的if分支語句中,添加了full的類,不會添加進 configCandidates 中,故為空
if (configCandidates.isEmpty()) {
	return;
}

// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
	int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
	int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
	return Integer.compare(i1, i2);
});

// Detect any custom bean name generation strategy supplied through the enclosing application context
SingletonBeanRegistry sbr = null;

//   現在我們使用的BeanDefinitionRegistry是其實是Spring的Bean工廠(DefaultListableBeanFactory)  他是SingletonBeanRegistry的子類的話
if (registry instanceof SingletonBeanRegistry) {
	//  將registry強轉為SingletonBeanRegistry
	sbr = (SingletonBeanRegistry) registry;
	if (!this.localBeanNameGeneratorSet) {
		//  是否有自定義的
		BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
		//SingletonBeanRegistry中有id為 org.springframework.context.annotation.internalConfigurationBeanNameGenerator
		//  如果有則利用他的,否則是spring默認的
		if (generator != null) {
			this.componentScanBeanNameGenerator = generator;
			this.importBeanNameGenerator = generator;
		}
	}
}

if (this.environment == null) {
	this.environment = new StandardEnvironment();
}

//   這是個配置類的解析器  會解析每一個添加了  @Configuration 的類
ConfigurationClassParser parser = new ConfigurationClassParser(
		this.metadataReaderFactory, this.problemReporter, this.environment,
		this.resourceLoader, this.componentScanBeanNameGenerator, registry);

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
	//   僅僅處理添加了@Configuration注解的類, 進行包掃描,跟進去
	parser.parse(candidates);
	//   運行到這里完成了掃描,BeanFactory中的BeanDefinitionMap中就多了我們字節添加進去的bean信息

	parser.validate();
	//map.keyset
	Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
	configClasses.removeAll(alreadyParsed);

	// Read the model and create bean definitions based on its content
	if (this.reader == null) {
		this.reader = new ConfigurationClassBeanDefinitionReader(
				registry, this.sourceExtractor, this.resourceLoader, this.environment,
				this.importBeanNameGenerator, parser.getImportRegistry());
	}

	this.reader.loadBeanDefinitions(configClasses);
	alreadyParsed.addAll(configClasses);

	candidates.clear();

...

跟進這個 parser.parse(candidates);,這里就來到了又一波高潮,准備開始包掃描了

不怕麻煩,再提一下,當前的這個對象就是我們的MianConfig,它是被AnnotatedBeanDefinitionReader讀取到的,所以它一定是AnnotatedBeanDefinition, 所以一定會進入到第一個if分支中

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<>();
    for (BeanDefinitionHolder holder : configCandidates) {
    BeanDefinition bd = holder.getBeanDefinition();
    try {
    	if (bd instanceof AnnotatedBeanDefinition) {
    		parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
    	}
    	else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
    		parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
    	}

繼續跟進,同樣是經過了幾個沒有重要邏輯的方法之后,進入到下面的方法中,看他是如何下面這個重要的方法中

這個方法主要做了兩件大事:

  • 處理掃描添加有@Component注解的普通類,並將它們直接添加到BeanFactoryMap
  • 掃描處理添加有@Import注解

看他首先取出所有的@CompoenntScan注解,循環遍歷注解,每次循環都使用this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());進行真正的包掃描

跟進這個方法中,可以看到它一開始就自己重新new 了一個包掃描器,然后解析當前循環的@ComponentScan注解上的其他如excludeFilters,includeFilters等屬性,最后開始真正的進行包掃描, 在這個掃描的過程中,會將命中符合條件的普通類(如被@Component標識),進行如下處理

  • 設置scope信息
  • 生成BeanName
  • 給掃描出來的這些添加上默認的屬性信息比如默認全是Lazy
  • 進一步,處理這些類上的注解信息,比如@Lazy , @Primary , @DependsOn , @Role , @Description,用這些信息覆蓋默認的信息
  • 將掃描出來的普通類直接添加到BeanFactoryMap中

完成了上面的普通類的掃描工作之后,下一個高潮就來了,處理@Import()的三中情況,它的解析我寫在如下代碼的下面


protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
			throws IOException {

		// Recursively process any member (nested) classes first
		//  遞歸地首先處理任何成員(嵌套)類
		processMemberClasses(configClass, sourceClass);

		// Process any @PropertySource annotations
		for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), PropertySources.class,
				org.springframework.context.annotation.PropertySource.class)) {
			if (this.environment instanceof ConfigurableEnvironment) {
				processPropertySource(propertySource);
			}
			else {
				logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
						"]. Reason: Environment must implement ConfigurableEnvironment");
			}
		}

		//   處理所有的@ComponentScan注解, 也就是讀取到了我們在MainConfig中使用@ConponentScans 中添加的元信息, 如value=com.changwu
		//   basePackages  lazyInit  userDefualtFileter ,,,  includeFileters excludeFilters  scopeResolver  nameGenerate ,,,
		// Process any @ComponentScan annotations
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			//   為什么要循環,以為 @ComponentScans(value={1,2,3}) value是一個數組
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				//   掃描com.changwu下面的普通類, 也就是添加了@Component注解的類, 然后將掃描出來的bean放到map中
				Set<BeanDefinitionHolder> scannedBeanDefinitions =
						this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

				// Check the set of scanned definitions for any further config classes and parse recursively if needed
				//檢查掃描出來的類當中是否還有configuration
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					//檢查    看看被掃描的普通類有沒有添加 配置相關的注解
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
		AnnotationAttributes importResource =
        ...

跟進上面的 processImports(configClass, sourceClass, getImports(sourceClass), true);方法,看他如何處理@Import注解,通過下面的代碼不難看出@Import注解存在三種情況,分別是

  • ImportSelector
  • ImportBeanDefinitionRegistrar
  • 普通類

第一種情況處理ImportSelector, 這個ImportSelector是很好用的組件,首先第一點: 我們可以通過自動ImportSelector完成類的批量注入,但是吧這個功能感覺就像是雞肋,棄之可惜,食之無味, 其實他還有一個妙用!配合jdk的動態代理我們可以實現類似AOP的切面,針對某一個對象進行動態的代理, 舉個例子: 自定義一個類,實現BeanPostProcessor接口,然后重寫它的public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { ... } 然后根據用戶名進行判斷,當找我們指定的用戶時,我們可以使用JDK的動態代理完成將這個對象轉換成代理對象,進而實現切面的增強

看一下它的處理,它判斷出@Import中含有ImportSelector.class時,就通過反射將這個對象創建出來代理對象,狸貓換太子,把代理對象交給Spring,得到ImportSelector的對象,具體反射出來的對象的實例就是程序員自定義的那個ImportSelector,得到這個對象之后,然后調用它的selectImports()方法,就返回了程序員指定的想批量導入DaoIOC中的對象的全類名, 下一步就是將這些類注入到IOC中,Spring的做法是遞歸調用, 因為上面說了,當前方法可以實現的三種Bean的注入,一般來說,通過 ImportSelecor導入的類就是普通類了, 會進入下面代碼中的最后一個else語句塊

第二種情況,處理ImportBeanDefinitionRegistrar,Spring的做法是,將它添加進一個map中

this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);

第三種情況,同樣和第一種情況是一樣的,也是先將信息放到map中

this.configurationClasses.put(configClass, configClass);

源碼如下:

	private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
			Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

		//   如果沒有添加了@Implot注解的類,直接退出去
		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) {
		//   情況1: 處理 @ImportSelector 注解
		if (candidate.isAssignable(ImportSelector.class)) {
			// Candidate class is an ImportSelector -> delegate to it to determine imports
			//   被循環獲取出現在Spring自己的以及掃描出來的全部的對象的Class描述
			Class<?> candidateClass = candidate.loadClass();

			//  只要這個對象的@Import(value= ImportSelector.clas)就被命中
			//  反射實現一個對象,反射創建的這個對象就是我們的手動添加的 繼承 ImportSelector 的那個對象
			ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
			ParserStrategyUtils.invokeAwareMethods(
					selector, this.environment, this.resourceLoader, this.registry);
			if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
				this.deferredImportSelectors.add(
						new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
			} else {
				//  回調反射出來的這個對象啊的 SelectImports() 方法,就能動態的獲取出我們手動添加進去的,准備批量注入的 對象的 ClassName 數組
				String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
				//   將importClassNames添加進一個list -- annotatedClasses中,然后返回出來
				Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
				//   遞歸調用, processImports()方法,顯然,再次遞歸的話,傳遞進去的importSourceClasses就是當前的類, 如果當前類是普通類,遞歸時就不再來到這里了, 而是進入下面的else代碼塊
				processImports(configClass, currentSourceClass, importSourceClasses, false);
			}
		} //    情況2:  處理@ImportBeanDefinitionRegistrar
		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);
			//  沒有和上面一樣進行回調,而是放入到一個list中
			configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
		}
		else {
			//   情況3: 處理普通類
			// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
			// process it as an @Configuration class
			// 否則,加入到importStack后調用 processConfigurationClass 進行處理
			//processConfigurationClass() 方法就在下面,  里面主要就是把類放到configurationClasses
			//configurationClasses是一個集合,會在后面拿出來解析成bd繼而注冊
			//可以看到普通類在掃描出來的時候就被注冊了
			//如果是importSelector,會先放到configurationClasses后面進行出來注冊
			this.importStack.registerImport(
					currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
			processConfigurationClass(candidate.asConfigClass(configClass));
		}
	}
}

當處理完成了三種@Import類型的導入方式之后,我們繼續往下看,三種方式都是忙着把讀取讀來的信息往map中放,那么在哪里進行處理的呢? 思考一下,接下來是不是得將讀取到的信息注冊進IOC中? 沒錯,我們退會到ConfigurationClassPostProcessor中的this.reader.loadBeanDefinitions(configClasses);方法中

同樣經過幾個沒有重要邏輯的方法之后,我們來到了ConfigurationClassBeanDefinitionReader中,着重它的loadBeanDefinitionsForConfigurationClass()方法, 源碼我貼在下面:

看看他做了什么, 一目了然,很清晰的思路,很牛逼很牛逼!!!

如果Spring發現,當前的類是被導入進來的,他按照Bean導入進來的方式進行注冊Bean,如果進給看一下,就能看熟悉的一幕,Spring使用Register進行Bean的注冊

如果Spring發現它有BeanMethod,也就是發現這個對象存在方法,換句話說發現我們的對象存在方法,就會進一步解析我們的方法,怎么解析方法呢? 按照對象的方法和類的方法分別解析,這也是為什么,當我們在配置類的靜態方法中使用@Bean進行注入對象,即使已經為MainConfig生成了代理,依然會出現重復注入對象的情況,但是BeanName不一樣哦,如果是靜態方法+@Bean, BeanName是當前的方法名

接下來的邏輯是 解析XML與處理Registrar

	private void loadBeanDefinitionsForConfigurationClass(
			ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {

		//   如果一個類是被  Import  進來的, 會在Spring進行標記,然后再這里完成注冊
		//   @Import(aaa.class) 那這個aaa就是被Import的,在這里完成注冊
		if (configClass.isImported()) {
			registerBeanDefinitionForImportedConfigurationClass(configClass);
		}
	
		for (BeanMethod beanMethod : configClass.getBeanMethods()) {
			loadBeanDefinitionsForBeanMethod(beanMethod);
		}

		 //  xml
		loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

		//    處理注冊Registrar 的邏輯
		loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
	}

代碼讀到這里其實已經深入很多層了,重要的邏輯也都過了一下,現在的工作就是層層的往回出棧,回到開始的PostProcessorRegistationDelegate中的invokeBeanFactoryPostProcessors()方法

上面的大部分篇幅都是當前方法中的 invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);方法層層遞歸進去的,現在我們從它開始往后看

剛開始不是說,一共是創建三個list嗎?然后又把其中的兩個進行了合並了, 那么接下來的工作就是去執行這兩個list中剩下的沒執行的邏輯, 沒執行的就是,Spring自己提供的和程序員添加的BeanFactoryPostProcessor的實現,沒錯就是執行重寫的BeanFactoryPostProcessor()postProcessBeanFactory()方法

//  registryProcessors  其實就是唯一的 ConfigurationClassPostProcessor
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);

//  自定義BeanFactoryPostProcessor   的 postProcessorBeanFactory()方法
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

大家看,上面的兩個方法是一樣,只不過是傳遞進去的參數不一樣而已,其實吧,高潮來了,如果大家還記得的話,應該猜到了現在的入參位置上的參數, 沒錯就是 ConfigurationClassPostProcessor這個類, 它太牛逼了! 同時實現了BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的抽象方法, 下面就去具體看一下它的實現,准備好了嗎? 來高潮了哦

源碼如下: 它的解析我寫在下面

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	int factoryId = System.identityHashCode(beanFactory);
	if (this.factoriesPostProcessed.contains(factoryId)) {
		throw new IllegalStateException(
				"postProcessBeanFactory already called on this post-processor against " + beanFactory);
	}
	this.factoriesPostProcessed.add(factoryId);
	if (!this.registriesPostProcessed.contains(factoryId)) {
		// BeanDefinitionRegistryPostProcessor hook apparently not supported...
		// Simply call processConfigurationClasses lazily at this point then.
		processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
	}

	enhanceConfigurationClasses(beanFactory);

	beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

enhanceConfigurationClasses(beanFactory); 這是在干什么? 跟大家說,這太牛了!!!為什么說它牛? 不買關子,它在這里開啟了JDK的動態代理

在這個方法中有一段判斷邏輯,如下: 這是很贊的一段代碼,感覺到了心跳的加速! 它判斷當前的這個BeanDefinition是不是full類型的, 關於這個Full的解釋,其實我們上面的描述中有說過,就是說,如果我們的MainConfig添加了@Configuration注解,它就被會標記為FUll, 被標記為full的話,就會在下面的代碼中產生cglib的動態代理,也就是說,我們獲取到的存入容器的MainConfig可以不是普通的原始對象, 而是被Cglib增強后的對象, 這有什么用呢? 用處可大了! 我們通常會在配置類中添加@Bean注解,注入對象,但是如果被添加了@Bean注解的方法彼此之間相互調用的戶,就會出現重復注入的現象,Spring通過下面的判斷,進行代理,不再給用戶原始的Mainconfig,這樣就實現對方法調用的控制,進而保證了百分百單例的情況

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
	BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
	//  判斷isFull, 看看是不是添加了@Configuration的全注解的類
	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.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {
			logger.warn("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'.");
		}
		//   如果是的話,放到這個linkedHashMap中
		configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
	}
}

接着往下看代碼就可以看到Spring底層使用原生cglib進行代理的邏輯了,

	private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(configSuperClass);
		enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
		enhancer.setUseFactory(false);
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
		enhancer.setCallbackFilter(CALLBACK_FILTER);
		enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
		return enhancer;
	}

這個過程中有幾個需要注意的地方,一般我們自己實現Cglib時, 都只是設置一個setSuperclass(XXX)然后對這個XXX進行增強,但是Spring沒這么簡單,它還enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class}); 想想,為什么還要這個接口呢?看下面的圖

enheanceConfiguration繼承體系圖

通過上面的圖可以看到,這個接口實現了beanFactoryAware,而這個beanFactory中存在setBeanFactory(BeanFactory bf) 怎么樣? 有思路沒?

整理一下思路,就是說,Spring的目的就是將程序員傳遞進來的MainConfig進行動態代理,為啥要代理呢? 因為有的程序員會故意搞破壞,會使用被@Bean標注的方法之間相互調用,導致Bean的多次注入,於是Spring想通過代理,返回給用戶一個代理對象,然后添加動態的判斷, 如果容器中已經存在bean了,就從容器中獲取,不再重復注入,沒有的話就注入進去一個

這就引出了為什么,代理對象需要一個BeanFactory,因為BeanDefinition都在BeanFactory中,這也是為什么上面需要setInterface()

接着enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader)); 設置一個生成策略, 因為我們生曾的代理類需要一個BeanFactory類型的變量啊,沒有這個引用,如何接受前面set的BeanFactory???

再往后的亮點就是enhancer.setCallbackFilter(CALLBACK_FILTER);設置回調的攔截器,看看有哪些攔截器呢? 代碼如下:

private static final Callback[] CALLBACKS = new Callback[] {
		//   第一個實現, 增強方法, 主要控制bean的作用域換句話說就是讓每一次調用方法不再去new,  跟進去看看
		new BeanMethodInterceptor(), //   他是當前類的內部類
		//設置一個beanFactory
		new BeanFactoryAwareMethodInterceptor(),
		NoOp.INSTANCE
};

我們跟進去BeanMethodInterceptor,這個類也很精彩,玩過cglib的人都知道需要一個inteceptor,而我們正在看的這個接口就實現了methodInterceptor,重不重要,你懂的...

直接看它的intercept()方法, 細細品味這個方法,很有味道哦!!!, 它的解析我寫在這個方法的下面

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
					MethodProxy cglibMethodProxy) throws Throwable {

	//    enhancedConfigInstance 是代理對象
	//   通過代理對象enhancedConfigInstance中cglib生成的成員變量$$beanFactory獲得beanFactory。
	ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);

	String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

	// Determine whether this bean is a scoped-proxy
	Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
	if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
		String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
		if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
			beanName = scopedBeanName;
		}
	}

	if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
			factoryContainsBean(beanFactory, beanName)) {

		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
		}
		else {
			return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
		}
	}

	if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
		// The factory is calling the bean method in order to instantiate and register the bean
		// (i.e. via a getBean() call) -> invoke the super implementation of the method to actually
		// create the bean instance.
		if (logger.isWarnEnabled() &&
				BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
			logger.warn(String.format("@Bean method %s.%s is non-static and returns an object " +
							"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
							"result in a failure to process annotations such as @Autowired, " +
							"@Resource and @PostConstruct within the method's declaring " +
							"@Configuration class. Add the 'static' modifier to this method to avoid " +
							"these container lifecycle issues; see @Bean javadoc for complete details.",
					beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
		}
		//   滿足條件 調用父類的構造方法new 對象
		return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
	}

	return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}

首先,它從代理對象中獲取出beanFactory

然后他處理 FactoryBean的情況,這個FactoryBean太牛了,因為當程序員把一個FactoryBean注入到IOC時,附帶的還會把另一個對象駐入進IOC, 它是如何進行區分判斷的呢? Spring會使用一個BeanFactory.FACTORY_BEAN_PREFIX == & 這個前綴去匹配, 比如userDao3()中調用了userDao4(), 他就是用&userDao4當成key去beanFactory中獲取,如果獲取獲取出對象了,說明這是個FactoryBean 需要對獲取出來的這個對象進一步生成代理

接下來判斷,是new 呢? 還是從Factory中獲取呢?

Spring的判斷依據是根據方法名,判斷調用方法和正在執行的方法是同一個方法,根據什么呢? 只要名字相同, 結論就是直接new

舉個例子:

userDao3(){}
// 它的調用方法和正在執行的方法是同一個方法,怎么相同呢? 名字相同, 結論就是直接new

userDao4(){
   userDao3()
}
userDao4()是調用方法, userDao3()執行方法  userDao4 和 userDao3 名字不一樣, 所以選擇getBean()

第二個例子:

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
	System.out.println("intercept.............");
	methodProxy.invokeSuper(o,objects);
	return null;
}

比如我們僅僅執行代理方法A, 這個A方法 就時上面的method 也是methodProxy, 但是如果我們在代理方法A中執行B方法, 這時 A == method != methodProxy == B

代碼看到這里,其實一開始的refresh()中的invokeBeanFactoryPostProcessors(beanFactory);方法就看完了, 着呢么樣刺激不?

有錯誤的話歡迎批評指出,有過對您有幫助,歡迎點贊支持


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM