劍指Spring原始碼(二)

CoderBear發表於2019-02-14

這是春節後的第一篇部落格,我在構思這篇部落格的時候,一度想放棄,想想要不要換個東西寫,因為畢竟個人水平有限,Spring原始碼實在博大精深,不是我這個菜的摳腳的菜雞可以駕馭的,怕誤人子弟,還有就是原始碼分析類的部落格實在是太難寫了,和一般的部落格真心不同,可能寫了很多,自己都不知道自己在寫些什麼,但是還是要堅持,從接觸部落格的那一天開始,就非常佩服那些大神,樂於分享,無私奉獻,我也從那些部落格中學到了不少東西,慢慢的從一個嫩嫩的小菜雞變成了禿頭大菜雞,其中最佩服的就是那些原始碼分析類的部落格,雖然看不懂,但是從部落格中,我分明讀出了大神們對技術的熱愛,對技術的堅持,對技術的執著。堅持!

在上一篇Spring原始碼解析中,我們分析了

	//根據引數型別可以知道,其實可以傳入多個annotatedClasses,但是這種情況出現的比較少
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		//呼叫無參建構函式,會先呼叫父類GenericApplicationContext的建構函式
		//父類的建構函式裡面就是初始化DefaultListableBeanFactory,並且賦值給beanFactory
		//本類的建構函式裡面,初始化了一個讀取器:AnnotatedBeanDefinitionReader read,一個掃描器ClassPathBeanDefinitionScanner scanner
		//scanner的用處不是很大,它僅僅是在我們外部手動呼叫 .scan 等方法才有用,常規方式是不會用到scanner物件的
		this();
		//把傳入的類進行註冊,這裡有兩個情況,
		//傳入傳統的配置類
		//傳入bean(雖然一般沒有人會這麼做
		//看到後面會知道spring把傳統的帶上@Configuration的配置類稱之為FULL配置類,不帶@Configuration的稱之為Lite配置類
		//但是我們這裡先把帶上@Configuration的配置類稱之為傳統配置類,不帶的稱之為普通bean
		register(annotatedClasses);
		//重新整理
		refresh();
	}
複製程式碼

中的前兩行程式碼,回顧下,這兩行程式碼,主要是把我們的配置類和內建的幾個後置處理器放到了兩個集合中:

				//beanDefinitionMap是Map<String, BeanDefinition>,
				//這裡就是把beanName作為key,beanDefinition作為value,推到map裡面
				this.beanDefinitionMap.put(beanName, beanDefinition);

				//beanDefinitionNames就是一個List<String>,這裡就是把beanName放到List中去
				this.beanDefinitionNames.add(beanName);
複製程式碼

今天,我們來分析下第三行程式碼,即:

		//重新整理
		refresh();
複製程式碼

這個方法做了很多事情,讓我們點開這個方法:

	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			//重新整理預處理,和主流程關係不大,就是儲存了容器的啟動時間,啟動標誌等
			prepareRefresh();

			//DefaultListableBeanFactory
			// Tell the subclass to refresh the internal bean factory.
			//和主流程關係也不大,最終獲得了DefaultListableBeanFactory,
			// DefaultListableBeanFactory實現了ConfigurableListableBeanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			//還是一些準備工作,新增了兩個後置處理器:ApplicationContextAwareProcessor,ApplicationListenerDetector
			//還設定了 忽略自動裝配 和 允許自動裝配 的介面,如果不存在某個bean的時候,spring就自動註冊singleton bean
			//還設定了bean表示式解析器 等
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				//這是一個空方法
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				//執行自定義的BeanFactoryProcessor和內建的BeanFactoryProcessor
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 註冊BeanPostProcessor
				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();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}
複製程式碼

裡面有很多小方法,我們今天的目標是分析前五個小方法:

prepareRefresh

從命名來看,就知道這個方法主要做了一些重新整理前的準備工作,和主流程關係不大,主要是儲存了容器的啟動時間,啟動標誌等。

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()

這個方法和主流程關係也不是很大,可以簡單的認為,就是把beanFactory取出來而已。

prepareBeanFactory

			//還是一些準備工作,新增了兩個後置處理器:ApplicationContextAwareProcessor,ApplicationListenerDetector
			//還設定了 忽略自動裝配 和 允許自動裝配 的介面,如果不存在某個bean的時候,spring就自動註冊singleton bean
			//還設定了bean表示式解析器 等
			prepareBeanFactory(beanFactory);
複製程式碼

這程式碼相比前面兩個就比較重要了,我們需要點進去好好看看,做了什麼操作:

	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// Tell the internal bean factory to use the context's class loader etc.
		beanFactory.setBeanClassLoader(getClassLoader());//設定類載入器

		//設定bean表示式解析器
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

		//屬性編輯器支援
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		// Configure the bean factory with context callbacks.
		//新增一個後置處理器:ApplicationContextAwareProcessor,此後置處理處理器實現了BeanPostProcessor介面
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

		//以下介面,忽略自動裝配
		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 interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
		//以下介面,允許自動裝配,第一個引數是自動裝配的型別,,第二個欄位是自動裝配的值
		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.
		//新增一個後置處理器:ApplicationListenerDetector,此後置處理器實現了BeanPostProcessor介面
		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名稱為XXX,spring就自己建立一個名稱為XXX的singleton bean
		//Register default environment beans.

		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}
複製程式碼

主要做了如下的操作:

  1. 設定了一個類載入器
  2. 設定了bean表示式解析器
  3. 新增了屬性編輯器的支援
  4. 新增了一個後置處理器:ApplicationContextAwareProcessor,此後置處理器實現了BeanPostProcessor介面
  5. 設定了一些忽略自動裝配的介面
  6. 設定了一些允許自動裝配的介面,並且進行了賦值操作
  7. 在容器中還沒有XX的bean的時候,幫我們註冊beanName為XX的singleton bean

postProcessBeanFactory(beanFactory)

				//這是一個空方法
				postProcessBeanFactory(beanFactory);

複製程式碼

這是一個空方法,可能以後Spring會進行擴充套件把。

invokeBeanFactoryPostProcessors(beanFactory)

				//執行自定義的BeanFactoryProcessor和內建的BeanFactoryProcessor
				invokeBeanFactoryPostProcessors(beanFactory);
複製程式碼

重點程式碼終於來了,可以說 這句程式碼是目前為止最重要,也是內容最多的程式碼了,我們有必要好好分析下:

	protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {

		//getBeanFactoryPostProcessors真是坑,第一次看到這裡的時候,愣住了,總覺得獲得的永遠都是空的集合,掉入坑裡,久久無法自拔
		//後來才知道spring允許我們手動新增BeanFactoryPostProcessor
		//即:annotationConfigApplicationContext.addBeanFactoryPostProcessor(XXX);
		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()));
		}
	}
複製程式碼

讓我們看看第一個小方法的第二個引數:

	public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
		return this.beanFactoryPostProcessors;
	}
複製程式碼

這裡獲得的是BeanFactoryPostProcessor,當我看到這裡的時候,愣住了,通過IDEA的查詢引用功能,我發現這個集合永遠都是空的,根本沒有程式碼為這個集合新增資料,很久都沒有想通,後來才知道我們在外部可以手動新增一個後置處理器,而不是交給Spring去掃描,即:

		AnnotationConfigApplicationContext annotationConfigApplicationContext =
				new AnnotationConfigApplicationContext(AppConfig.class);
		annotationConfigApplicationContext.addBeanFactoryPostProcessor(XXX);
複製程式碼

只有這樣,這個集合才不會為空,但是應該沒有人這麼做吧,當然也有可能是我孤陋寡聞。

讓我們點開invokeBeanFactoryPostProcessors方法:

	public static void invokeBeanFactoryPostProcessors(
			ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

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

		//beanFactory是DefaultListableBeanFactory,是BeanDefinitionRegistry的實現類,所以肯定滿足if
		if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

			//regularPostProcessors 用來存放BeanFactoryPostProcessor,
			List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();

			//registryProcessors 用來存放BeanDefinitionRegistryPostProcessor
			//BeanDefinitionRegistryPostProcessor擴充套件了BeanFactoryPostProcessor
			List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

			// 迴圈傳進來的beanFactoryPostProcessors,正常情況下,beanFactoryPostProcessors肯定沒有資料
			// 因為beanFactoryPostProcessors是獲得手動新增的,而不是spring掃描的
			// 只有手動呼叫annotationConfigApplicationContext.addBeanFactoryPostProcessor(XXX)才會有資料
			for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
				// 判斷postProcessor是不是BeanDefinitionRegistryPostProcessor,因為BeanDefinitionRegistryPostProcessor
				// 擴充套件了BeanFactoryPostProcessor,所以這裡先要判斷是不是BeanDefinitionRegistryPostProcessor
				// 是的話,直接執行postProcessBeanDefinitionRegistry方法,然後把物件裝到registryProcessors裡面去
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryProcessor.postProcessBeanDefinitionRegistry(registry);
					registryProcessors.add(registryProcessor);
				}

				else {//不是的話,就裝到regularPostProcessors
					regularPostProcessors.add(postProcessor);
				}
			}

			// Do not initialize FactoryBeans here: We need to leave all regular beans
			// uninitialized to let the bean factory post-processors apply to them!
			// Separate between BeanDefinitionRegistryPostProcessors that implement
			// PriorityOrdered, Ordered, and the rest.
			//一個臨時變數,用來裝載BeanDefinitionRegistryPostProcessor
			//BeanDefinitionRegistry繼承了PostProcessorBeanFactoryPostProcessor
			List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

			// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
			// 獲得實現BeanDefinitionRegistryPostProcessor介面的類的BeanName:org.springframework.context.annotation.internalConfigurationAnnotationProcessor
			// 並且裝入陣列postProcessorNames,我理解一般情況下,只會找到一個
			// 這裡又有一個坑,為什麼我自己建立了一個實現BeanDefinitionRegistryPostProcessor介面的類,也打上了@Component註解
			// 配置類也加上了@Component註解,但是這裡卻沒有拿到
			// 因為直到這一步,Spring還沒有去掃描,掃描是在ConfigurationClassPostProcessor類中完成的,也就是下面的第一個
			// invokeBeanDefinitionRegistryPostProcessors方法
			String[] postProcessorNames =
					beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

			for (String ppName : postProcessorNames) {
				if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
					//獲得ConfigurationClassPostProcessor類,並且放到currentRegistryProcessors
					//ConfigurationClassPostProcessor是很重要的一個類,它實現了BeanDefinitionRegistryPostProcessor介面
					//BeanDefinitionRegistryPostProcessor介面又實現了BeanFactoryPostProcessor介面
					//ConfigurationClassPostProcessor是極其重要的類
					//裡面執行了掃描Bean,Import,ImportResouce等各種操作
					//用來處理配置類(有兩種情況 一種是傳統意義上的配置類,一種是普通的bean)的各種邏輯
					currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
					//把name放到processedBeans,後續會根據這個集合來判斷處理器是否已經被執行過了
					processedBeans.add(ppName);
				}
			}

			//處理排序
			sortPostProcessors(currentRegistryProcessors, beanFactory);

			//合併Processors,為什麼要合併,因為registryProcessors是裝載BeanDefinitionRegistryPostProcessor的
			//一開始的時候,spring只會執行BeanDefinitionRegistryPostProcessor獨有的方法
			//而不會執行BeanDefinitionRegistryPostProcessor父類的方法,即BeanFactoryProcessor的方法
			//所以這裡需要把處理器放入一個集合中,後續統一執行父類的方法
			registryProcessors.addAll(currentRegistryProcessors);

			//可以理解為執行ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法
			//Spring熱插播的體現,像ConfigurationClassPostProcessor就相當於一個元件,Spring很多事情就是交給元件去管理
			//如果不想用這個元件,直接把註冊元件的那一步去掉就可以
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

			//因為currentRegistryProcessors是一個臨時變數,所以需要清除
			currentRegistryProcessors.clear();

			// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
			// 再次根據BeanDefinitionRegistryPostProcessor獲得BeanName,看這個BeanName是否已經被執行過了,有沒有實現Ordered介面
			// 如果沒有被執行過,也實現了Ordered介面的話,把物件推送到currentRegistryProcessors,名稱推送到processedBeans
			// 如果沒有實現Ordered介面的話,這裡不把資料加到currentRegistryProcessors,processedBeans中,後續再做處理
			// 這裡才可以獲得我們定義的實現了BeanDefinitionRegistryPostProcessor的Bean
			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);

			//合併Processors
			registryProcessors.addAll(currentRegistryProcessors);

			//執行我們自定義的BeanDefinitionRegistryPostProcessor
			invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

			//清空臨時變數
			currentRegistryProcessors.clear();

			// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
			// 上面的程式碼是執行了實現了Ordered介面的BeanDefinitionRegistryPostProcessor,
			// 下面的程式碼就是執行沒有實現Ordered介面的BeanDefinitionRegistryPostProcessor
			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();
			}

			// Now, invoke the postProcessBeanFactory callback of all processors handled so far.
			//registryProcessors集合裝載BeanDefinitionRegistryPostProcessor
			//上面的程式碼是執行子類獨有的方法,這裡需要再把父類的方法也執行一次
			invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);

			//regularPostProcessors裝載BeanFactoryPostProcessor,執行BeanFactoryPostProcessor的方法
			//但是regularPostProcessors一般情況下,是不會有資料的,只有在外面手動新增BeanFactoryPostProcessor,才會有資料
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
		}

		else {
			// Invoke factory processors registered with the context instance.
			invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let the bean factory post-processors apply to them!
		//找到BeanFactoryPostProcessor實現類的BeanName陣列
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

		// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		//迴圈BeanName陣列
		for (String ppName : postProcessorNames) {
			//如果這個Bean被執行過了,跳過
			if (processedBeans.contains(ppName)) {
				// skip - already processed in first phase above
			}
			//如果實現了PriorityOrdered介面,加入到priorityOrderedPostProcessors
			else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
			}
			//如果實現了Ordered介面,加入到orderedPostProcessorNames
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			//如果既沒有實現PriorityOrdered,也沒有實現Ordered。加入到nonOrderedPostProcessorNames
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		//排序處理priorityOrderedPostProcessors,即實現了PriorityOrdered介面的BeanFactoryPostProcessor
		// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		//執行priorityOrderedPostProcessors
		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

		//執行實現了Ordered介面的BeanFactoryPostProcessor
		// Next, invoke the BeanFactoryPostProcessors that implement Ordered.
		List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
		for (String postProcessorName : orderedPostProcessorNames) {
			orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

		// 執行既沒有實現PriorityOrdered介面,也沒有實現Ordered介面的BeanFactoryPostProcessor
		// Finally, invoke all other BeanFactoryPostProcessors.
		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
		for (String postProcessorName : nonOrderedPostProcessorNames) {
			nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

		// Clear cached merged bean definitions since the post-processors might have
		// modified the original metadata, e.g. replacing placeholders in values...
		beanFactory.clearMetadataCache();
	}
複製程式碼

首先判斷beanFactory是不是BeanDefinitionRegistry的例項,當然肯定是的,然後執行如下操作:

  1. 定義了一個Set,裝載BeanName,後面會根據這個Set,來判斷後置處理器是否被執行過了。

  2. 定義了兩個List,一個是regularPostProcessors,用來裝載BeanFactoryPostProcessor,一個是registryProcessors用來裝載BeanDefinitionRegistryPostProcessor,其中BeanDefinitionRegistryPostProcessor擴充套件了BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor有兩個方法,一個是獨有的postProcessBeanDefinitionRegistry方法,一個是父類的postProcessBeanFactory方法。

  3. 迴圈傳進來的beanFactoryPostProcessors,上面已經解釋過了,一般情況下,這裡永遠都是空的,只有手動add beanFactoryPostProcessor,這裡才會有資料。我們假設beanFactoryPostProcessors有資料,進入迴圈,判斷postProcessor是不是BeanDefinitionRegistryPostProcessor,因為BeanDefinitionRegistryPostProcessor擴充套件了BeanFactoryPostProcessor,所以這裡先要判斷是不是BeanDefinitionRegistryPostProcessor,是的話,執行postProcessBeanDefinitionRegistry方法,然後把物件裝到registryProcessors裡面去,不是的話,就裝到regularPostProcessors。

  4. 定義了一個臨時變數:currentRegistryProcessors,用來裝載BeanDefinitionRegistryPostProcessor。

  5. getBeanNamesForType,顧名思義,是根據型別查到BeanNames,這裡有一點需要注意,就是去哪裡找,點開這個方法的話,就知道是迴圈beanDefinitionNames去找,這個方法以後也會經常看到。這裡傳了BeanDefinitionRegistryPostProcessor.class,就是找到型別為BeanDefinitionRegistryPostProcessor的後置處理器,並且賦值給postProcessorNames。一般情況下,只會找到一個,就是org.springframework.context.annotation.internalConfigurationAnnotationProcessor,也就是ConfigurationAnnotationProcessor。這個後置處理器在上一節中已經說明過了,十分重要。這裡有一個問題,為什麼我自己寫了個類,實現了BeanDefinitionRegistryPostProcessor介面,也打上了@Component註解,但是這裡沒有獲得,因為直到這一步,Spring還沒有完成掃描,掃描是在ConfigurationClassPostProcessor類中完成的,也就是下面第一個invokeBeanDefinitionRegistryPostProcessors方法。

  6. 迴圈postProcessorNames,其實也就是org.springframework.context.annotation.internalConfigurationAnnotationProcessor,判斷此後置處理器是否實現了PriorityOrdered介面(ConfigurationAnnotationProcessor也實現了PriorityOrdered介面), 如果實現了,把它新增到currentRegistryProcessors這個臨時變數中,再放入processedBeans,代表這個後置處理已經被處理過了。當然現在還沒有處理,但是馬上就要處理了。。。

  7. 進行排序,PriorityOrdered是一個排序介面,如果實現了它,就說明此後置處理器是有順序的,所以需要排序。當然目前這裡只有一個後置處理器,就是ConfigurationClassPostProcessor。

  8. 把currentRegistryProcessors合併到registryProcessors,為什麼需要合併?因為一開始spring只會執行BeanDefinitionRegistryPostProcessor獨有的方法,而不會執行BeanDefinitionRegistryPostProcessor父類的方法,即BeanFactoryProcessor介面中的方法,所以需要把這些後置處理器放入一個集合中,後續統一執行BeanFactoryProcessor介面中的方法。當然目前這裡只有一個後置處理器,就是ConfigurationClassPostProcessor。

  9. 可以理解為執行currentRegistryProcessors中的ConfigurationClassPostProcessor中的postProcessBeanDefinitionRegistry方法,這就是Spring設計思想的體現了,在這裡體現的就是其中的熱插拔,外掛化開發的思想。Spring中很多東西都是交給外掛去處理的,這個後置處理器就相當於一個外掛,如果不想用了,直接不新增就是了。這個方法特別重要,我們後面會詳細說來。

  10. 清空currentRegistryProcessors,因為currentRegistryProcessors是一個臨時變數,已經完成了目前的使命,所以需要清空,當然後面還會用到。

  11. 再次根據BeanDefinitionRegistryPostProcessor獲得BeanName,然後進行迴圈,看這個後置處理器是否被執行過了,如果沒有被執行過,也實現了Ordered介面的話,把此後置處理器推送到currentRegistryProcessors和processedBeans中。 這裡就可以獲得我們定義的,並且打上@Component註解的後置處理器了,因為Spring已經完成了掃描,但是這裡需要注意的是,由於ConfigurationClassPostProcessor在上面已經被執行過了,所以雖然可以通過getBeanNamesForType獲得,但是並不會加入到currentRegistryProcessors和processedBeans。

  12. 處理排序。

  13. 合併Processors,合併的理由和上面是一樣的。

  14. 執行我們自定義的BeanDefinitionRegistryPostProcessor。

  15. 清空臨時變數。

  16. 在上面的方法中,僅僅是執行了實現了Ordered介面的BeanDefinitionRegistryPostProcessor,這裡是執行沒有實現Ordered介面的BeanDefinitionRegistryPostProcessor。

  17. 上面的程式碼是執行子類獨有的方法,這裡需要再把父類的方法也執行一次。

  18. 執行regularPostProcessors中的後置處理器的方法,需要注意的是,在一般情況下,regularPostProcessors是不會有資料的,只有在外面手動新增BeanFactoryPostProcessor,才會有資料。

  19. 查詢實現了BeanFactoryPostProcessor的後置處理器,並且執行後置處理器中的方法。和上面的邏輯差不多,不再詳細說明。

這就是這個方法中做的主要的事情了,可以說是比較複雜的。但是邏輯還是比較清晰的,在第9步的時候,我說有一個方法會詳細說來,現在就讓我們好好看看這個方法究竟做了什麼吧。

	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
		String[] candidateNames = registry.getBeanDefinitionNames();//獲得所有的BeanDefinition的Name,放入candidateNames陣列

		//迴圈candidateNames陣列
		for (String beanName : candidateNames) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);//根據beanName獲得BeanDefinition

			// 內部有兩個標記位來標記是否已經處理過了
			// 這裡會引發一連串知識盲點
			// 當我們註冊配置類的時候,可以不加Configuration註解,直接使用Component ComponentScan Import ImportResource註解,稱之為Lite配置類
			// 如果加了Configuration註解,就稱之為Full配置類
			// 如果我們註冊了Lite配置類,我們getBean這個配置類,會發現它就是原本的那個配置類
			// 如果我們註冊了Full配置類,我們getBean這個配置類,會發現它已經不是原本那個配置類了,而是已經被cgilb代理的類了
			// 寫一個A類,其中有一個構造方法,列印出“你好”
			// 再寫一個配置類,裡面有兩個bean註解的方法
			// 其中一個方法new了A 類,並且返回A的物件,把此方法稱之為getA
			// 第二個方法又呼叫了getA方法
			// 如果配置類是Lite配置類,會發現列印了兩次“你好”,也就是說A類被new了兩次
			// 如果配置類是Full配置類,會發現只列印了一次“你好”,也就是說A類只被new了一次,因為這個類被cgilb代理了,方法已經被改寫
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
				if (logger.isDebugEnabled()) {
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}

			//判斷是否為配置類(有兩種情況 一種是傳統意義上的配置類,一種是普通的bean),
			//在這個方法內部,會做判斷,這個配置類是Full配置類,還是Lite配置類,並且做上標記
			//滿足條件,加入到configCandidates
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// 如果沒有配置類,直接返回
		// Return immediately if no @Configuration classes were found
		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;
		// DefaultListableBeanFactory最終會實現SingletonBeanRegistry介面,所以可以進入到這個if
		if (registry instanceof SingletonBeanRegistry) {
			sbr = (SingletonBeanRegistry) registry;
			if (!this.localBeanNameGeneratorSet) {
				//spring中可以修改預設的bean命名方式,這裡就是看使用者有沒有自定義bean命名方式,雖然一般沒有人會這麼做
				BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
				if (generator != null) {
					this.componentScanBeanNameGenerator = generator;
					this.importBeanNameGenerator = generator;
				}
			}
		}

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

		// Parse each @Configuration class
		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 {
			//解析配置類(傳統意義上的配置類或者是普通bean,核心來了)
			parser.parse(candidates);
			parser.validate();

			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);//直到這一步才把Import的類,@Bean @ImportRosource 轉換成BeanDefinition
			alreadyParsed.addAll(configClasses);//把configClasses加入到alreadyParsed,代表

			candidates.clear();
			//獲得註冊器裡面BeanDefinition的數量 和 candidateNames進行比較
			//如果大於的話,說明有新的BeanDefinition註冊進來了
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
				String[] newCandidateNames = registry.getBeanDefinitionNames();//從註冊器裡面獲得BeanDefinitionNames
				Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));//candidateNames轉換set
				Set<String> alreadyParsedClasses = new HashSet<>();
				//迴圈alreadyParsed。把類名加入到alreadyParsedClasses
				for (ConfigurationClass configurationClass : alreadyParsed) {
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
				for (String candidateName : newCandidateNames) {
					if (!oldCandidateNames.contains(candidateName)) {
						BeanDefinition bd = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(bd.getBeanClassName())) {
							candidates.add(new BeanDefinitionHolder(bd, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
			sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
			// Clear cache in externally provided MetadataReaderFactory; this is a no-op
			// for a shared cache since it'll be cleared by the ApplicationContext.
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}
複製程式碼
  1. 獲得所有的BeanName,放入candidateNames陣列。
  2. 迴圈candidateNames陣列,根據beanName獲得BeanDefinition,判斷此BeanDefinition是否已經被處理過了。
  3. 判斷是否是配置類,如果是的話。加入到configCandidates陣列,在判斷的時候,還會標記配置類屬於Full配置類,還是Lite配置類,這裡會引發一連串的知識盲點: 3.1 當我們註冊配置類的時候,可以不加@Configuration註解,直接使用@Component @ComponentScan @Import @ImportResource等註解,Spring把這種配置類稱之為Lite配置類, 如果加了@Configuration註解,就稱之為Full配置類。 3.2 如果我們註冊了Lite配置類,我們getBean這個配置類,會發現它就是原本的那個配置類,如果我們註冊了Full配置類,我們getBean這個配置類,會發現它已經不是原本那個配置類了,而是已經被cgilb代理的類了。 3.3 寫一個A類,其中有一個構造方法,列印出“你好”,再寫一個配置類,裡面有兩個被@bean註解的方法,其中一個方法new了A類,並且返回A的物件,把此方法稱之為getA,第二個方法又呼叫了getA方法,如果配置類是Lite配置類,會發現列印了兩次“你好”,也就是說A類被new了兩次,如果配置類是Full配置類,會發現只列印了一次“你好”,也就是說A類只被new了一次,因為這個類被cgilb代理了,方法已經被改寫。 3.4 具體的可以看我的這篇部落格:juejin.im/post/5c637d… 裡面有詳細的說明。
  4. 如果沒有配置類直接返回。
  5. 處理排序。
  6. 解析配置類,可能是Full配置類,也有可能是Lite配置類,這個小方法是此方法的核心,稍後具體說明。
  7. 在第6步的時候,只是註冊了部分Bean,像 @Import @Bean等,是沒有被註冊的,這裡統一對這些進行註冊。

下面是解析配置類的過程:

	public void parse(Set<BeanDefinitionHolder> configCandidates) {
		this.deferredImportSelectors = new LinkedList<>();
		//迴圈傳進來的配置類
		for (BeanDefinitionHolder holder : configCandidates) {
			BeanDefinition bd = holder.getBeanDefinition();//獲得BeanDefinition
			try {
				//如果獲得BeanDefinition是AnnotatedBeanDefinition的例項
				if (bd instanceof AnnotatedBeanDefinition) {
					parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
				} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
					parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
				} else {
					parse(bd.getBeanClassName(), holder.getBeanName());
				}
			} catch (BeanDefinitionStoreException ex) {
				throw ex;
			} catch (Throwable ex) {
				throw new BeanDefinitionStoreException(
						"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
			}
		}

		//執行DeferredImportSelector
		processDeferredImportSelectors();
	}
複製程式碼

因為可以有多個配置類,所以需要迴圈處理。我們的配置類的BeanDefinition是AnnotatedBeanDefinition的例項,所以會進入第一個if:

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
		processConfigurationClass(new ConfigurationClass(metadata, beanName));
	}
複製程式碼
	protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {

		//判斷是否需要跳過
		if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
			return;
		}

		ConfigurationClass existingClass = this.configurationClasses.get(configClass);
		if (existingClass != null) {
			if (configClass.isImported()) {
				if (existingClass.isImported()) {
					existingClass.mergeImportedBy(configClass);
				}
				// Otherwise ignore new imported config class; existing non-imported class overrides it.
				return;
			} else {
				// Explicit bean definition found, probably replacing an import.
				// Let's remove the old one and go with the new one.
				this.configurationClasses.remove(configClass);
				this.knownSuperclasses.values().removeIf(configClass::equals);
			}
		}

		// Recursively process the configuration class and its superclass hierarchy.
		SourceClass sourceClass = asSourceClass(configClass);
		do {
			sourceClass = doProcessConfigurationClass(configClass, sourceClass);
		}
		while (sourceClass != null);

		this.configurationClasses.put(configClass, configClass);
	}
複製程式碼

重點在於doProcessConfigurationClass方法,需要特別注意,最後一行程式碼,會把configClass放入一個Map,會在上面第7步中用到。

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

		//遞迴處理內部類,一般不會寫內部類
		// Recursively process any member (nested) classes first
		processMemberClasses(configClass, sourceClass);

		// Process any @PropertySource annotations
		//處理@PropertySource註解,@PropertySource註解用來載入properties檔案
		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");
			}
		}

		// Process any @ComponentScan annotations
		//獲得ComponentScan註解具體的內容,ComponentScan註解除了最常用的basePackage之外,還有includeFilters,excludeFilters等
		Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
				sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);

		//如果沒有打上ComponentScan,或者被@Condition條件跳過,就不再進入這個if
		if (!componentScans.isEmpty() &&
				!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
			//迴圈處理componentScans
			for (AnnotationAttributes componentScan : componentScans) {
				// The config class is annotated with @ComponentScan -> perform the scan immediately
				//componentScan就是@ComponentScan上的具體內容,sourceClass.getMetadata().getClassName()就是配置類的名稱
				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
				for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
					BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
					if (bdCand == null) {
						bdCand = holder.getBeanDefinition();
					}
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
						//遞迴呼叫,因為可能元件類有被@Bean標記的方法,或者元件類本身也有ComponentScan等註解
						parse(bdCand.getBeanClassName(), holder.getBeanName());
					}
				}
			}
		}

		// Process any @Import annotations
		//處理@Import註解
		//@Import註解是spring中很重要的一個註解,Springboot大量應用這個註解
		//@Import三種類,一種是Import普通類,一種是Import ImportSelector,還有一種是Import ImportBeanDefinitionRegistrar
		//getImports(sourceClass)是獲得import的內容,返回的是一個set
		processImports(configClass, sourceClass, getImports(sourceClass), true);

		// Process any @ImportResource annotations
		//處理@ImportResource註解
		AnnotationAttributes importResource =
				AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
		if (importResource != null) {
			String[] resources = importResource.getStringArray("locations");
			Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
			for (String resource : resources) {
				String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
				configClass.addImportedResource(resolvedResource, readerClass);
			}
		}

		//處理@Bean的方法,可以看到獲得了帶有@Bean的方法後,不是馬上轉換成BeanDefinition,而是先用一個set接收
		// Process individual @Bean methods
		Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
		for (MethodMetadata methodMetadata : beanMethods) {
			configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
		}

		// Process default methods on interfaces
		processInterfaces(configClass, sourceClass);

		// Process superclass, if any
		if (sourceClass.getMetadata().hasSuperClass()) {
			String superclass = sourceClass.getMetadata().getSuperClassName();
			if (superclass != null && !superclass.startsWith("java") &&
					!this.knownSuperclasses.containsKey(superclass)) {
				this.knownSuperclasses.put(superclass, configClass);
				// Superclass found, return its annotation metadata and recurse
				return sourceClass.getSuperClass();
			}
		}

		// No superclass -> processing is complete
		return null;
	}
複製程式碼
  1. 遞迴處理內部類,一般不會使用內部類。
  2. 處理@PropertySource註解,@PropertySource註解用來載入properties檔案。
  3. 獲得ComponentScan註解具體的內容,ComponentScan註解除了最常用的basePackage之外,還有includeFilters,excludeFilters等。
  4. 判斷有沒有被@ComponentScans標記,或者被@Condition條件帶過,如果滿足條件的話,進入if,進行如下操作: 4.1 執行掃描操作,把掃描出來的放入set,這個方法稍後再詳細說明。 4.2 迴圈set,判斷是否是配置類,是的話,遞迴呼叫parse方法,因為被掃描出來的類,還是一個配置類,有@ComponentScans註解,或者其中有被@Bean標記的方法 等等,所以需要再次被解析。
  5. 處理@Import註解,@Import是Spring中很重要的一個註解,正是由於它的存在,讓Spring非常靈活,不管是Spring內部,還是與Spring整合的第三方技術,都大量的運用了@Import註解,@Import有三種情況,一種是Import普通類,一種是Import ImportSelector,還有一種是Import ImportBeanDefinitionRegistrar,getImports(sourceClass)是獲得import的內容,返回的是一個set,這個方法稍後再詳細說明。
  6. 處理@ImportResource註解。
  7. 處理@Bean的方法,可以看到獲得了帶有@Bean的方法後,不是馬上轉換成BeanDefinition,而是先用一個set接收。

我們先來看4.1中的那個方法:

	public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
		//掃描器,還記不記在new AnnotationConfigApplicationContext的時候
		//會呼叫AnnotationConfigApplicationContext的構造方法
		//構造方法裡面有一句 this.scanner = new ClassPathBeanDefinitionScanner(this);
		//當時說這個物件不重要,這裡就是證明了。常規用法中,實際上執行掃描的只會是這裡的scanner物件
		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
				componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

		//判斷是否重寫了預設的命名規則
		Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
		boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
		scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
				BeanUtils.instantiateClass(generatorClass));

		ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
		if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
			scanner.setScopedProxyMode(scopedProxyMode);
		}
		else {
			Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
			scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
		}

		scanner.setResourcePattern(componentScan.getString("resourcePattern"));

		//addIncludeFilter addExcludeFilter,最終是往List<TypeFilter>裡面填充資料
		//TypeFilter是一個函式式介面,函式式介面在java8的時候大放異彩,只定義了一個虛方法的介面被稱為函式式介面
		//當呼叫scanner.addIncludeFilter  scanner.addExcludeFilter 僅僅把 定義的規則塞進去,並麼有真正去執行匹配過程

		//處理includeFilters
		for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
				scanner.addIncludeFilter(typeFilter);
			}
		}

		//處理excludeFilters
		for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
			for (TypeFilter typeFilter : typeFiltersFor(filter)) {
				scanner.addExcludeFilter(typeFilter);
			}
		}

		boolean lazyInit = componentScan.getBoolean("lazyInit");
		if (lazyInit) {
			scanner.getBeanDefinitionDefaults().setLazyInit(true);
		}

		Set<String> basePackages = new LinkedHashSet<>();
		String[] basePackagesArray = componentScan.getStringArray("basePackages");
		for (String pkg : basePackagesArray) {
			String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
					ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
			Collections.addAll(basePackages, tokenized);
		}
		// 從下面的程式碼可以看出ComponentScans指定掃描目標,除了最常用的basePackages,還有兩種方式
		// 1.指定basePackageClasses,就是指定多個類,只要是與這幾個類同級的,或者在這幾個類下級的都可以被掃描到,這種方式其實是spring比較推薦的
		// 因為指定basePackages沒有IDE的檢查,容易出錯,但是指定一個類,就有IDE的檢查了,不容易出錯,經常會用一個空的類來作為basePackageClasses
		// 2.直接不指定,預設會把與配置類同級,或者在配置類下級的作為掃描目標
		for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
			basePackages.add(ClassUtils.getPackageName(clazz));
		}
		if (basePackages.isEmpty()) {
			basePackages.add(ClassUtils.getPackageName(declaringClass));
		}

		//把規則填充到排除規則:List<TypeFilter>,這裡就把 註冊類自身當作排除規則,真正執行匹配的時候,會把自身給排除
		scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
			@Override
			protected boolean matchClassName(String className) {
				return declaringClass.equals(className);
			}
		});
		//basePackages是一個LinkedHashSet<String>,這裡就是把basePackages轉為字串陣列的形式
		return scanner.doScan(StringUtils.toStringArray(basePackages));
	}
複製程式碼
  1. 定義了一個掃描器scanner,還記不記在new AnnotationConfigApplicationContext的時候,會呼叫AnnotationConfigApplicationContext的構造方法,構造方法裡面有一句 this.scanner = new ClassPathBeanDefinitionScanner(this);當時說這個物件不重要,這裡就是證明了。常規用法中,實際上執行掃描的只會是這裡的scanner物件。
  2. 處理includeFilters,就是把規則新增到scanner。
  3. 處理excludeFilters,就是把規則新增到scanner。
  4. 解析basePackages,獲得需要掃描哪些包。
  5. 新增一個預設的排除規則:排除自身。
  6. 執行掃描,稍後詳細說明。

這裡需要做一個補充說明,新增規則的時候,只是把具體的規則放入規則類的集合中去,規則類是一個函式式介面,只定義了一個虛方法的介面被稱為函式式介面,函式式介面在java8的時候大放異彩,這裡只是把規則方塞進去,並沒有真正執行匹配規則。

我們來看看到底是怎麼執行掃描的:

	protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
		//迴圈處理basePackages
		for (String basePackage : basePackages) {
			//根據包名找到符合條件的BeanDefinition集合
			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
			for (BeanDefinition candidate : candidates) {
				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
				candidate.setScope(scopeMetadata.getScopeName());
				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
				//由findCandidateComponents內部可知,這裡的candidate是ScannedGenericBeanDefinition
				//而ScannedGenericBeanDefinition是AbstractBeanDefinition和AnnotatedBeanDefinition的之類
				//所以下面的兩個if都會進入
				if (candidate instanceof AbstractBeanDefinition) {
					//內部會設定預設值
					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
				}
				if (candidate instanceof AnnotatedBeanDefinition) {
					//如果是AnnotatedBeanDefinition,還會再設定一次值
					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
				}
				if (checkCandidate(beanName, candidate)) {
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
複製程式碼

因為basePackages可能有多個,所以需要迴圈處理,最終會進行Bean的註冊。下面再來看看findCandidateComponents方法:

	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		//spring支援component索引技術,需要引入一個元件,因為大部分情況不會引入這個元件
		//所以不會進入到這個if
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			return scanCandidateComponents(basePackage);
		}
	}
複製程式碼

Spring支援component索引技術,需要引入一個元件,大部分專案沒有引入這個元件,所以會進入scanCandidateComponents方法:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			//把 傳進來的類似 名稱空間形式的字串轉換成類似類檔案地址的形式,然後在前面加上classpath*:
			//即:com.xx=>classpath*:com/xx/**/*.class
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			//根據packageSearchPath,獲得符合要求的檔案
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			//迴圈資源
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}

				if (resource.isReadable()) {//判斷資源是否可讀,並且不是一個目錄
					try {
						//metadataReader 後設資料讀取器,解析resource,也可以理解為描述資源的資料結構
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						//在isCandidateComponent方法內部會真正執行匹配規則
						//註冊配置類自身會被排除,不會進入到這個if
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setResource(resource);
							sbd.setSource(resource);
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}
複製程式碼
  1. 把傳進來的類似名稱空間形式的字串轉換成類似類檔案地址的形式,然後在前面加上classpath*,即:com.xx=>classpath*:com/xx/**/*.class。
  2. 根據packageSearchPath,獲得符合要求的檔案。
  3. 迴圈符合要求的檔案,進一步進行判斷。 最終會把符合要求的檔案,轉換為BeanDefinition,並且返回。

直到這裡,上面說的4.1中提到的方法終於分析完畢了,讓我們再看看上面提到的第5步中的處理@Import註解方法:

	//這個方法內部相當相當複雜,importCandidates是Import的內容,呼叫這個方法的時候,已經說過可能有三種情況
	//這裡再說下,1.Import普通類,2.Import ImportSelector,3.Import ImportBeanDefinitionRegistrar
	//迴圈importCandidates,判斷屬於哪種情況
	//如果是普通類,會進到else,呼叫processConfigurationClass方法
	//這個方法是不是很熟悉,沒錯,processImports這個方法就是在processConfigurationClass方法中被呼叫的
	//processImports又主動呼叫processConfigurationClass方法,是一個遞迴呼叫,因為Import的普通類,也有可能被加了Import註解,@ComponentScan註解 或者其他註解,所以普通類需要再次被解析
	//如果Import ImportSelector就跑到了第一個if中去,首先執行Aware介面方法,所以我們在實現ImportSelector的同時,還可以實現Aware介面
	//然後判斷是不是DeferredImportSelector,DeferredImportSelector擴充套件了ImportSelector
	//如果不是的話,呼叫selectImports方法,獲得全限定類名陣列,在轉換成類的陣列,然後再呼叫processImports,又特麼的是一個遞迴呼叫...
	//可能又有三種情況,一種情況是selectImports的類是一個普通類,第二種情況是selectImports的類是一個ImportBeanDefinitionRegistrar類,第三種情況是還是一個ImportSelector類...
	//所以又需要遞迴呼叫
	//如果Import ImportBeanDefinitionRegistrar就跑到了第二個if,還是會執行Aware介面方法,這裡終於沒有遞迴了,會把資料放到ConfigurationClass中的Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars中去
	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) {
					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);
						if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
							this.deferredImportSelectors.add(
									new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
						} else {
							String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
							Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
							processImports(configClass, currentSourceClass, importSourceClasses, false);
						}
					} 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);
						configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
					} else {
						// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
						// process it as an @Configuration class
						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();
			}
		}
	}
複製程式碼

這個方法大概的作用已經在註釋中已經寫明瞭,就不再重複了。

直到這裡,才把ConfigurationClassPostProcessor中的processConfigBeanDefinitions方法簡單的過了一下。

但是這還沒有結束,processConfigBeanDefinitions是BeanDefinitionRegistryPostProcessor介面中的方法,BeanDefinitionRegistryPostProcessor擴充套件了BeanFactoryPostProcessor,還有postProcessBeanFactory方法沒有分析,這個方法是幹嘛的,簡單的來說,就是判斷配置類是Lite配置類,還是Full配置類,如果是配置類,就會被Cglib代理,目的就是保證Bean的作用域。關於這個方法實在是比較複雜,限於篇幅原因,這裡就不再繼續了,有興趣的朋友可以到我的GitHub看下,裡面有簡單的分析(當然其實還有很多東西無法在部落格中體現,如果都貼出來的話,實在是太長了,但是在GitHub上面都有註釋)。

我們來做一個總結,ConfigurationClassPostProcessor中的processConfigBeanDefinitions方法十分重要,主要是完成掃描,最終註冊我們定義的Bean。

相關文章