1. 程式人生 > >做一個合格的程式猿之淺析Spring IoC原始碼(十一)Spring refresh()方法解析之一

做一個合格的程式猿之淺析Spring IoC原始碼(十一)Spring refresh()方法解析之一

經過上面幾節的簡單介紹我們瞭解了spring的一些元件,現在我們來分析一下AbstractApplicationContext中的refresh()這個核心方法吧~

用我們上一節的程式碼,debug進入refresh方法:

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 準備beanfactory來使用這個上下文.做一些準備工作,例如classloader,beanfactoryPostProcessor等
			prepareBeanFactory(beanFactory);

			try {
				//允許上下文的子類去執行postProcessor
				postProcessBeanFactory(beanFactory);

				// 開始執行註冊到該上下文的BeanFactoryPostProcessors
				invokeBeanFactoryPostProcessors(beanFactory);

				// 開始註冊BeanPostProcessor來攔截其他的bean的初始化過程
				registerBeanPostProcessors(beanFactory);

				// 初始化訊息源
				initMessageSource();

				//註冊上下文事件的廣播集
				initApplicationEventMulticaster();

				//初始化一些特殊的bean
				onRefresh();

				//查詢並校驗監聽器並註冊
				registerListeners();

				// 例項化所有非懶載入的所有bean
				finishBeanFactoryInitialization(beanFactory);

				//最後一步釋出所有的運用
				finishRefresh();
			}

			catch (BeansException ex) {
				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

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

				// Propagate exception to caller.
				throw ex;
			}
		}
	}

結合上一節我們給出的spring的一些元件例項化的初始化順序圖:


我們先看invokeBeanFactoryPostProcessors(beanFactory)這個方法

<span style="color:#000000;">protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		// 先執行BeanDefinitionRegistryPostProcessors,因為我們這邊沒有實現這個介面,先暫時忽略,這段程式碼
		Set<String> processedBeans = new HashSet<String>();
		if (beanFactory instanceof BeanDefinitionRegistry) {
			BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
			List<BeanFactoryPostProcessor> regularPostProcessors = new LinkedList<BeanFactoryPostProcessor>();
			List<BeanDefinitionRegistryPostProcessor> registryPostProcessors =
					new LinkedList<BeanDefinitionRegistryPostProcessor>();
			for (BeanFactoryPostProcessor postProcessor : getBeanFactoryPostProcessors()) {
				if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
					BeanDefinitionRegistryPostProcessor registryPostProcessor =
							(BeanDefinitionRegistryPostProcessor) postProcessor;
					registryPostProcessor.postProcessBeanDefinitionRegistry(registry);
					registryPostProcessors.add(registryPostProcessor);
				}
				else {
					regularPostProcessors.add(postProcessor);
				}
			}
			Map<String, BeanDefinitionRegistryPostProcessor> beanMap =
					beanFactory.getBeansOfType(BeanDefinitionRegistryPostProcessor.class, true, false);
			List<BeanDefinitionRegistryPostProcessor> registryPostProcessorBeans =
					new ArrayList<BeanDefinitionRegistryPostProcessor>(beanMap.values());
			OrderComparator.sort(registryPostProcessorBeans);
			for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessorBeans) {
				postProcessor.postProcessBeanDefinitionRegistry(registry);
			}
			invokeBeanFactoryPostProcessors(registryPostProcessors, beanFactory);
			invokeBeanFactoryPostProcessors(registryPostProcessorBeans, beanFactory);
			invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
			processedBeans.addAll(beanMap.keySet());
		}
		else {
			// Invoke factory processors registered with the context instance.
			invokeBeanFactoryPostProcessors(getBeanFactoryPostProcessors(), beanFactory);
		}

		//在beanFactory中獲取實現BeanFactoryPostProcessor這個介面的bean的名稱
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

		// 將獲取到的BeanFactoryPostProcessors的bean分類,根據這些bean是否也實現了PriorityOrdered,Ordered這些介面,或者其他,因為我們的例子中
		// 並沒有實現這些介面,所以我們的"springMultiBean"這個在spring-init.xml中(程式碼見上一節)定義的bean將進入nonOrderedPostProcessorNames.add(ppName)這個程式碼塊
		List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
		List<String> orderedPostProcessorNames = new ArrayList<String>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
		for (String ppName : postProcessorNames) {
			if (processedBeans.contains(ppName)) {
				// skip - already processed in first phase above
			}
			else if (isTypeMatch(ppName, PriorityOrdered.class)) {
				priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
			}
			else if (isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		//首先先執行實現“權重排序”的BeanFactoryPostProcessors,我們的bean“springMultiBean”並沒有實現,所以不管
		OrderComparator.sort(priorityOrderedPostProcessors);
		invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

		// 然後執行實現“排序”的介面的BeanFactoryPostProcessors,我們也沒有實現,暫時不看這塊程式碼塊
		List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
		for (String postProcessorName : orderedPostProcessorNames) {
			orderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		OrderComparator.sort(orderedPostProcessors);
		invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

		// 最後執行“其他”的BeanFactoryPostProcessors,這邊就即將執行我們的springMultiBean這個bean的BeanFactoryPostProcessors
		// 這邊新建了一個List型別是BeanFactoryPostProcessor,好了,這邊就開始需要例項化實現BeanFactoryPostProcessor的bean了,否則,
		// 無法放入List<BeanFactoryPostProcessor> 也就是說,如果想先執行BeanFactoryPostProcessor的方法,必須先例項化實現該介面的bean
		List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
		//可能我們的配置檔案有很多的bean實現了BeanFactoryPostProcessors,迴圈bean的名字
		for (String postProcessorName : nonOrderedPostProcessorNames) {
		    //底下的方法塊是核心塊,getBean這個方法就是初始化實現BeanFactoryPostProcessor這個介面的bean了,例項化好了之後放入List<BeanFactoryPostProcessor>
			nonOrderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class));
		}
		//批量執行nonOrderedPostProcessors中的BeanFactoryPostProcessor
		invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
	}</span>

好了,到目前為止,我們應該知道了上一節的spring Bean例項化順序的執行截圖了:


也就是我們的上次總結的結論:

①首先執行的是建構函式

②然後執行的BeanNameAware這個介面中的方法

③然後執行的是BeanFactoryAware這個介面中的方法

④執行InitializingBean介面中的afterPropertiesSet的方法

⑤執行我們在xml中定義的init-method這個方法

⑥最後執行的是BeanFactoryPostProcessor這個方法

分析了invokeBeanFactoryPostProcessors這個方法,我們知道這個方法就是去執行BeanFactoryPostProcessor

這個介面中的方法去的,上面程式碼註釋也清楚的寫到如果想先執行BeanFactoryPostProcessor這個介面的方法,必須先去例項化實現這個介面的Bean,也就是getBean這個方法了,即在執行⑥之前,①~⑤這些方法全是在getBean這個方法中執行的:


我們接著分析一下getBean這個超級核心的方法:

<span style="color:#000000;">@SuppressWarnings("unchecked")
	protected <T> T doGetBean(
			final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
			throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
			if (logger.isDebugEnabled()) {
				if (isSingletonCurrentlyInCreation(beanName)) {
					logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
							"' that is not fully initialized yet - a consequence of a circular reference");
				}
				else {
					logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
				}
			}
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
		}

		else {
			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

			// Check if bean definition exists in this factory.
			BeanFactory parentBeanFactory = getParentBeanFactory();
			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
				// Not found -> check parent.
				String nameToLookup = originalBeanName(name);
				if (args != null) {
					// Delegation to parent with explicit args.
					return (T) parentBeanFactory.getBean(nameToLookup, args);
				}
				else {
					// No args -> delegate to standard getBean method.
					return parentBeanFactory.getBean(nameToLookup, requiredType);
				}
			}

			if (!typeCheckOnly) {
				markBeanAsCreated(beanName);
			}

			try {
				final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
				checkMergedBeanDefinition(mbd, beanName, args);

				// Guarantee initialization of beans that the current bean depends on.
				String[] dependsOn = mbd.getDependsOn();
				if (dependsOn != null) {
					for (String dependsOnBean : dependsOn) {
						getBean(dependsOnBean);
						registerDependentBean(dependsOnBean, beanName);
					}
				}

				// Create bean instance.
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
						public Object getObject() throws BeansException {
							try {
								return createBean(beanName, mbd, args);
							}
							catch (BeansException ex) {
								// Explicitly remove instance from singleton cache: It might have been put there
								// eagerly by the creation process, to allow for circular reference resolution.
								// Also remove any beans that received a temporary reference to the bean.
								destroySingleton(beanName);
								throw ex;
							}
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

				else if (mbd.isPrototype()) {
					// It's a prototype -> create a new instance.
					Object prototypeInstance = null;
					try {
						beforePrototypeCreation(beanName);
						prototypeInstance = createBean(beanName, mbd, args);
					}
					finally {
						afterPrototypeCreation(beanName);
					}
					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
				}

				else {
					String scopeName = mbd.getScope();
					final Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
							public Object getObject() throws BeansException {
								beforePrototypeCreation(beanName);
								try {
									return createBean(beanName, mbd, args);
								}
								finally {
									afterPrototypeCreation(beanName);
								}
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; " +
								"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
			}
			catch (BeansException ex) {
				cleanupAfterBeanCreationFailure(beanName);
				throw ex;
			}
		}

		// Check if required type matches the type of the actual bean instance.
		if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
			try {
				return getTypeConverter().convertIfNecessary(bean, requiredType);
			}
			catch (TypeMismatchException ex) {
				if (logger.isDebugEnabled()) {
					logger.debug("Failed to convert bean '" + name + "' to required type [" +
							ClassUtils.getQualifiedName(requiredType) + "]", ex);
				}
				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
			}
		}
		return (T) bean;
	}
</span>
好吧~,這個getBean的具體實現貌似有點長,一步步分析吧

第一部分:



這個方法首先先去singleton快取中去找例項(這裡我們肯定沒找到,應該我們沒有把我們的bean手動放入singletonObjects這個Map裡面去)

第二部分:


這段程式碼是先獲取該beanFactory父factory,希望從這些factory中獲取,如果該beanfactory有父類,則希望用父類去例項化該bean,我們這邊的beanfactory為null,暫不討論~

接著看第三部分:


上圖中

第一部分先標記目前的bean的正在建立

第二部分獲取根據beanName該bean在beanfactory中的beanDefinitionMap的BeanDefinition,然後去獲取這個bean依賴的bean,如果依賴的bean還沒有建立,則先建立依賴的bean,遞迴呼叫,(Dependence Inject依賴注入的概念吧),如果找不到依賴,則忽略

第三部分:如果是單例(我們暫時只討論如何建立單例)

則呼叫createBean()這個方法

好了,我們暫不看Prototype建立的過程,我們接著跟蹤createBean()


上圖中

第①部分:確保該bean的class是真實存在的,也就是該bean是可以classload可以找到載入的

第②部分:準備方法的重寫

第③部分(很重要):請注意,這邊有個return,也就是說這邊可以返回bean了,但看註釋:Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.這邊就很清晰了,我們以前在beanPostProcessor的章節講過,beanPostProcessor是可以臨時修改bean的,它的優先順序高於正常例項化bean的(也就是第四部分例項化的方法),如果beanPostProcessor能返回,則直接返回了,這邊程式碼下次分析,我們還是先分析主要流程:

看第四部分:

doCreateBean(beanName, mbd, args)這個方法

這個方法首先初始化一個BeanWrapper,然後再看createBeanInstance()


這塊程式碼主要是再次對bean做安全檢查並確定該bean有預設的建構函式,createBeanInstance()這個方法最後一行

此時建構函式列印了我們system的內容,也就是第一個列印的


回到doCreateBean()這個方法

初始化bean,的確,現在bean已經例項化了,開始初始化該bean,進入initializeBean(...)這個方法


執行aware方法(看來beanFactoryAware和beanNameAware要執行了)


看原始碼果然:

接著


開始執行初始化方法


首先先判斷該bean是否實現了InitializingBean,如果實現了先執行afterPropertiesSet這個方法,然後如果該bean又執行了init-method,事實的確如此:

好了,到此為止,我們的第一個名為“springMultiBean"已經初始化了,例項化好的大體步驟我們已經基本瞭解了,大家先體會一下,我們spring-init.xml中還有一個bean”springOtherBean“沒講解,下次分解~正好一起重新分析細節~


相關推薦

一個合格程式淺析Spring IoC原始碼Spring refresh()方法解析後記1

上次分析refresh這塊spring IoC的時候,時間比較倉促,只是debug了部分原始碼,大家分析起來不是很好~ 今天我們還是先總結一下吧~ spring在例項化bean的時候,根據bean

一個合格程式淺析Spring IoC原始碼Spring refresh()方法解析之一

經過上面幾節的簡單介紹我們瞭解了spring的一些元件,現在我們來分析一下AbstractApplicationContext中的refresh()這個核心方法吧~ 用我們上一節的程式碼,debug進入refresh方法: public void refresh() th

Spring 原始碼Spring流程彙總

Spring 容器初始化流程大致流程如下: this():註冊內建的BeanPostProcessor的BeanDefinition到容器 register(annotatedClasses):註冊配置類 BeanDefinition 到容器 prepareRefresh():初始化前的準備工作,列如對系

一個合格程式淺析Spring IoC原始碼Spring Bean的初始化順序

上幾節我們比較詳細地說明了一下BeanFactoryPostProcessor和BeanPostProcessor這2個介面的作用和意義,並且也花了一個章節,講了一下BeanFactory和FactoryBean的關係,最後我們也稍微說明了一下BeanFactoryAwar

一個合格程式淺析Spring AOP原始碼 分析JdkDynamicAopProxy的invoke方法

 上一節我們已經分析了Proxyfactorybean如何去生成一個目標物件的代理的,這一節我們將淺析一下基於JDK動態代理的核心回撥方法invoke的原始碼: 首先先開啟JdkDynamicAop

SpringSpring事物

一個 簡單介紹 數據 數據庫操作 nag 原子性 pre spring pointcut 事務是訪問數據庫的一個操作序列,DB應用系統通過事務集來完成對數據的存取。 事務必須遵循4個原則,即常說的 ACID A,Automicity,原子性,即事務要麽被全部執行

機器學習numpy和matplotlib學習

今天繼續來學習numpy。 學習有關複數矩陣在numpy中的建立和使用。 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Author : SundayCoder-俊勇 # @File : numpy3.py import

機器學習python入門指南numpy常用方法簡介

  numpy庫的安裝: window下命令列直接輸入pip install numpy 匯入numpy庫:import numpy 或者 import numpy as np numpy與list: 相同之處: 都可以用下標訪問元素,如a[3]. 都可以

各種音視訊編解碼學習詳解 編解碼學習筆記:Flash Video系列

 最近在研究音視訊編解碼這一塊兒,看到@bitbit大神寫的【各種音視訊編解碼學習詳解】這篇文章,非常感謝,佩服的五體投地。奈何大神這邊文章太長,在這裡我把它分解成很多小的篇幅,方便閱讀。大神部落格傳送門:https://www.cnblogs.com/skyofbitbit/p/3651

Spring Security原始碼分析Spring Security OAuth2整合JWT

Json web token (JWT), 是為了在網路應用環境間傳遞宣告而執行的一種基於JSON的開放標準(RFC 7519).該token被設計為緊湊且安全的,特別適用於分散式站點的單點登入(SSO)場景。JWT的宣告一般被用來在身份提供者和服務提供者

Spring事務處理

        Spring的宣告式事務處理的即開即用特性為使用者提供了很大的方便,在使用Spring時,我們絕大多數情況下還是使用其宣告式事務處理。宣告式事務處理涉及Spring框架對事務處理的統一管理,以及對併發事務和事務屬性的處理,是一個比較複雜的過程,下面瞭解一

Spring Boot:Spring Boot使用單元測試

前言這次來介紹下Spring Boot中對單元測試的整合使用,本篇會通過以下4點來介紹,基本滿足日常需求Service層單元測試Controller層單元測試新斷言assertThat使用單元測試的回滾正文Spring Boot中引入單元測試很簡單,依賴如下:1 2 3 4

Spring學習筆記AOP的註解方式cglib代理

JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎,切點,切面,如何定義切點,前置、後置、放回、異常、環繞通知 1.切點、切面 紅色的地方就是切面,增加額外的功能 連線點+增加功能的位置 = 切點 2.專案結構

spring學習——spring官方文件閱讀5.0.7——spring的@Bean與@Configuration註解

@Bean與@Configuration註解 @Bean註解用於方法上,返回的例項將由Spring IOC管理,當在@Configuration註解的類中使用@Bean註解時,@Bean相當於<bean/>元素,@Configuration相當於<bean

HBase 系列—— Spring/Spring Boot + Mybatis + Phoenix 整合

一、前言 使用 Spring+Mybatis 操作 Phoenix 和操作其他的關係型資料庫(如 Mysql,Oracle)在配置上是基本相同的,下面會分別給出 Spring/Spring Boot 整合步驟,完整程式碼見本倉庫: Spring + Mybatis + Phoenix SpringBoot

Spring Boot 入門:整合 WebSocket, 實時顯示系統日誌

以前面的部落格為基礎,最近一篇為Spring Boot 入門(十):整合Redis哨兵模式,實現Mybatis二級快取。本篇部落格主要介紹了Spring Boot整合 Web Socket進行日誌的推送,並實時顯示在頁面上。 1.匯入jar包 第一個jar包是websocket的,第二個jar包是關於環形佇列

spring-boot-route資料庫配置資訊加密

Spring Boot最大的特點就是自動配置了,大大的減少了傳統Spring框架的繁瑣配置,通過幾行簡單的配置就可以完成其他元件的接入。比如你想要連線mysql資料庫,只需要的配置檔案裡面加入mysql的一些配置資訊就可以了。為了保護資料的安全性,越來越多的公司選擇加密這些重要資訊。接下來一起來看看如何實現配

spring-boot-routespring-boot-admin監控服務

`SpringBootAdmin`不是Spring官方提供的模組,它包含了`Client`和`Server`兩部分。server部分提供了使用者管理介面,client即為被監控的服務。client需要註冊到server端。 SpringBootAdmin提供了很少的幾個監控服務端點,需要依賴SpringBo

程式---C語言細節9巨集定義、maxa,b巨集定義細節、大小端判斷、(int&)a什麼意思

主要內容:巨集定義、max(a,b)巨集定義細節、大小端判斷、(int&)a什麼意思 #if 1 #include <stdio.h> // 注意空格 #define F (x) ((x) - 1) // F代表後面 #define F(x)

年風雨,一個普通程式設計師的成長再見,2019。你好,2020!

00 回顧2019 在過去的這一年裡,收穫最大的便是眼界了, 與高水平的同事們一起合作,讓人愉快。 與高水平的領導共事,則讓人受益匪淺。 2019年元旦前後那段時間,突然陷入了焦慮,買了一些極客時間的課程,加入了一些知識星球。也確實提升了自己不少關於職場、工作、生活的一些認知。 但是收貨最大的還是在工作之餘,