Spring容器IOC初始化過程---今天終於進行總結了

擁抱心中的夢想發表於2018-03-22

一、老規矩,先比比點么蛾子

作為一個經常使用Spring的後端程式設計師,小編很早就想徹底弄懂整個Spring框架了!但它整體是非常大的,所有繼承圖非常複雜,加上小編修行尚淺,顯得力不從心。不過,男兒在世當立志,今天就先從Spring IOC容器的初始化開始說起,即使完成不了對整個Spring框架的完全掌握,也不丟人,因為小編動手了,穩住,我們能贏!

下面說一些閱讀前的建議:

  • 1、閱讀原始碼分析是非常無聊的,但既然你進來了,肯定也是對這個東西進行了解,也希望這篇總結能對你有所啟發。
  • 2、前方高能,文章可能會非常的長,圖文並茂。
  • 3、閱讀前建議你對相關設計模式、軟體設計6大原則有所瞭解,小編會在行文中進行穿插。
  • 4、小編在讀大四,學識尚淺,喜歡專研,如果你發現文章觀點有所錯誤或者與你見解有差異,歡迎評論區指出和交流!
  • 5、建議你邊看文章的時候可以邊在IDE中進行除錯跟蹤
  • 6、文章所有UML圖利用idea自動生成,具體生成方法為:選中一個類名,先ctrl+shift+alt+U,再ctrl+alt+B,然後回車即可

二、文章將圍繞什麼來進行展開?

不多,就一行程式碼,如下圖:

Spring容器IOC初始化過程---今天終於進行總結了

這句是Spring初始化的程式碼,雖然只有一句程式碼,但內容賊多!

三、Spring 容器IOC 有哪些東西組成?

這樣子,小編先理清下思路,一步一步來:

  • 1、上面那句程式碼有個檔案叫applicationContext.xml,這是個資原始檔,由於我們的bean都在裡邊進行配置定義,那Spring總得對這個檔案進行讀取並解析吧!所以Spring中有個模組叫Resource模組,顧名思義,就是資源嘛!用於對所有資源xml、txt、property等檔案資源的抽象。關於對Resource的更多知識,可以參考下邊兩篇文章:

談一談我對Spring Resource的理解

Spring資原始檔剖析和策略模式應用(李剛)

下面先貼一張小編生成的類圖(圖片有點大,不知道會不會不清晰,如果不清晰可以按照上面說的idea生成方法去生成即可)

Spring容器IOC初始化過程---今天終於進行總結了

可以看到Resource是整個體系的根介面,點進原始碼可以看到它定義了許多的策略方法,因為它是用了策略模式這種設計模式,運用的好處就是策略介面/類定義了同一的策略,不同的子類有不同的具體策略實現,客戶端呼叫時傳入一個具體的實現物件比如UrlResource或者FileSystemResource策略介面/類Resource即可!

所有策略如下:

Spring容器IOC初始化過程---今天終於進行總結了

  • 2、上面講了Spring框架對各種資源的抽象採用了策略模式,那麼問題來了,現在表示資源的東西有了,那麼是怎麼把該資源載入進來呢?於是就有了下面的ResourceLoader元件,該元件負責對Spring資源的載入,資源指的是xmlproperties等檔案資源,返回一個對應型別的Resource物件。。UML圖如下:

Spring容器IOC初始化過程---今天終於進行總結了

從上面的UML圖可以看出,ResourceLoader元件其實跟Resource元件差不多,都是一個根介面,對應有不同的子類實現,比如載入來自檔案系統的資源,則可以使用FileSystemResourceLoader,載入來自ServletContext上下文的資源,則可以使用ServletContextResourceLoader。 還有最重要的一點,從上圖看出,ApplicationContext,AbstractApplication是實現了ResourceLoader的,這說明什麼呢?說明我們的應用上下文ApplicationContext擁有載入資源的能力,這也說明了為什麼可以通過傳入一個String resource pathClassPathXmlApplicationContext("applicationContext.xml")就能獲得xml檔案資源的原因了!清晰了嗎?nice!

  • 3、上面兩點講到了,好!既然我們擁有了載入器ResourceLoader,也擁有了對資源的描述Resource,但是我們在xml檔案中宣告的<bean/>標籤在Spring又是怎麼表示的呢?注意這裡只是說對bean的定義,而不是說如何將<bean/>轉換為bean物件。我想應該不難理解吧!就像你想表示一個學生Student,那麼你在程式中肯定要宣告一個類Student吧!至於學生資料是從excel匯入,或者程式執行時new出來,或者從xml中載入進來這些都不重要,重要的是你要有一個將現實中的實體表示為程式中的物件的東西,所以<bean/>也需要在Spring中做一個定義!於是就引入一個叫BeanDefinition的元件,UML圖如下:

Spring容器IOC初始化過程---今天終於進行總結了

下面講解下UML圖:

首先配置檔案中的<bean/>標籤跟我們的BeanDefinition是一一對應的,<bean>元素標籤擁有classscopelazy-init等配置屬性,BeanDefinition則提供了相應的beanClassscopelazyInit屬性。

其中RootBeanDefinition是最常用的實現類,它對應一般性的<bean>元素標籤,GenericBeanDefinition是自2.5以後新加入的bean檔案配置屬性定義類,是一站式服務類。在配置檔案中可以定義父<bean>和子<bean>,父<bean>RootBeanDefinition表示,而子<bean>ChildBeanDefiniton表示,而沒有父<bean><bean>就使用RootBeanDefinition表示。AbstractBeanDefinition對兩者共同的類資訊進行抽象。 Spring通過BeanDefinition將配置檔案中的<bean>配置資訊轉換為容器的內部表示,並將這些BeanDefiniton註冊到BeanDefinitonRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置資訊的記憶體資料庫,主要是以map的形式儲存,後續操作直接從BeanDefinitionRegistry中讀取配置資訊。一般情況下,BeanDefinition只在容器啟動時載入並解析,除非容器重新整理或重啟,這些資訊不會發生變化,當然如果使用者有特殊的需求,也可以通過程式設計的方式在執行期調整BeanDefinition的定義。

  • 4、有了載入器ResourceLoader,也擁有了對資源的描述Resource,也有了對bean的定義,我們不禁要問,我們的Resource資源是怎麼轉成我們的BeanDefinition的呢?因此就引入了BeanDefinitionReader元件,Reader嘛!就是一種讀取機制,UML圖如下:

Spring容器IOC初始化過程---今天終於進行總結了

從上面可以看出,Spring 對reader進行了抽象,具體的功能交給其子類去實現,不同的實現對應不同的類,如PropertiedBeanDefinitionReader,XmlBeanDefinitionReader對應從Property和xml的Resource解析成BeanDefinition

其實這種讀取資料轉換成內部物件的,不僅僅是Spring專有的,比如:Dom4j解析器SAXReader reader = new SAXReader(); Document doc = reader.read(url.getFile());//url是一個URLResource物件 嚴格來說,都是Reader體系吧,就是將統一資源資料物件讀取轉換成相應內部物件。

  • 5、好了!基本上所有元件都快齊全了!對了,還有一個元件,你有了BeanDefinition後,你還必須將它們註冊到工廠中去,所以當你使用getBean()方法時工廠才知道返回什麼給你。還有一個問題,既然要儲存註冊這些bean,那肯定要有個資料結構充當容器吧!沒錯,就是一個Map,下面貼出BeanDefinitionRegistry的一個實現,叫SimpleBeanDefinitionRegistry的原始碼圖:

Spring容器IOC初始化過程---今天終於進行總結了

BeanDefinitionRegistry的UML圖如下:

Spring容器IOC初始化過程---今天終於進行總結了

從圖中可以看出,BeanDefinitionRegistry有三個預設實現,分別是SimpleBeanDefinitionRegistryDefaultListableBeanFactoryGenericApplicationContext,其中SimpleBeanDefinitionRegistryDefaultListableBeanFactory都持有一個Map,也就是說這兩個實現類把儲存了bean。而GenericApplicationContext則持有一個DefaultListableBeanFactory物件引用用於獲取裡邊對應的Map。 在DefaultListableBeanFactory

Spring容器IOC初始化過程---今天終於進行總結了

GenericApplicationContext

Spring容器IOC初始化過程---今天終於進行總結了

  • 6、前面說的5個點基本上可以看出ApplicationContext上下文基本直接或間接貫穿所有的部分,因此我們一般稱之為容器,除此之外,ApplicationContext還擁有除了bean容器這種角色外,還包括了獲取整個程式執行的環境引數等資訊(比如JDK版本,jre等),其實這部分Spring也做了對應的封裝,稱之為Enviroment,下面就跟著小編的eclipse,一起debug下容器的初始化工程吧!

Spring容器IOC初始化過程---今天終於進行總結了

四、實踐是檢驗真理的唯一標準

學生類Student.java如下:

package com.wokao666;

public class Student {

	private int id;
	private String name;
	private int age;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public Student(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public Student() {
		super();
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}

}
複製程式碼

application.xml中進行配置,兩個bean:

<bean id="stu1" class="com.wokao666.Student">
 	<property name="id" value="1"></property>
 	<property name="name" value="xiaoming"></property>
 	<property name="age" value="21"></property>
 </bean>
  <bean id="stu2" class="com.wokao666.Student">
 	<property name="id" value="2"></property>
 	<property name="name" value="xiaowang"></property>
 	<property name="age" value="22"></property>
 </bean>
複製程式碼

好了,接下來給最開頭那段程式碼打個斷點(Breakpoint):

第一步:急切地載入ContextClosedEvent類,以避免在WebLogic 8.1中的應用程式關閉時出現奇怪的類載入器問題。

Spring容器IOC初始化過程---今天終於進行總結了

這一步無需太過在意!

第二步:既然是new ClassPathXmlApplicationContext() 那麼就呼叫構造器嘛!

Spring容器IOC初始化過程---今天終於進行總結了

第三步:

Spring容器IOC初始化過程---今天終於進行總結了

第四步:

好,我們跟著第三步中的super(parent),再結合上面第三節的第6小點UML圖一步一步跟蹤,然後我們來到AbstractApplicationContext的這個方法:

Spring容器IOC初始化過程---今天終於進行總結了

那麼裡邊的resourcePatternResolver的型別是什麼呢?屬於第三節說的6大步驟的哪個部分呢?通過跟蹤可以看到它的型別是ResourcePatternResolver型別的,而ResourcePatternResolver又是繼承了ResourceLoader介面,因此屬於載入資源模組,如果還不清晰,我們們再看看ResourcePatternResolver的原始碼即可,如下圖:

Spring容器IOC初始化過程---今天終於進行總結了

對吧!不僅繼承ResourceLoader介面,而且只定義一個getResources()方法用於返回Resource[]資源集合。再者,這個介面還使用了策略模式,其具體的實現都在實現類當中,好吧!來看看UML圖就知道了!

Spring容器IOC初始化過程---今天終於進行總結了

PathMatchingResourcePatternResolver這個實現類呢!它就是用來解釋不同路徑資源的,比如你傳入的資源路徑有可能是一個常規的url,又或者有可能是以classpath*字首,都交給它處理。

ServletContextResourcePatternResolver這個實現類顧名思義就是用來載入Servlet上下文的,通常用在web中。

第五步:

接著第四步的方法,我們在未進入第四步的方法時,此時會對AbstractApplicationContext進行例項化,此時this物件的某些屬性被初始化了(如日誌物件),如下圖:

Spring容器IOC初始化過程---今天終於進行總結了

接著進入getResourcePatternResolver()方法:

Spring容器IOC初始化過程---今天終於進行總結了

第四步說了,PathMatchingResourcePatternResolver用來處理不同的資源路徑的,怎麼處理,我們先進去看看!

Spring容器IOC初始化過程---今天終於進行總結了

如果找到,此時控制檯會列印找到用於OSGi包URL解析的Equinox FileLocator日誌。沒列印很明顯找不到!

執行完成返回setParent()方法。

第六步:

Spring容器IOC初始化過程---今天終於進行總結了

如果父代是非null,,則該父代與當前this應用上下文環境合併。顯然這一步並沒有做什麼事!parent顯然是null的,那麼就不合並嘛!還是使用當前this的環境。

做個總結:前六步基本上做了兩件事:

  • 1、初始化相關上下文環境,也就是初始化ClassPathXmlApplicationContext例項
  • 2、獲得一個resourcePatternResolver物件,方便第七步的資源解析成Resource物件

第七步:

Spring容器IOC初始化過程---今天終於進行總結了

第七步又回到剛開始第三步的程式碼,因為我們前面6步已經完成對super(parent)的追蹤。讓我們看看setConfigLocation()方法是怎麼一回事~

        /**
	 * Set the config locations for this application context.//未應用上下文設定資源路徑
	 * <p>If not set, the implementation may use a default as appropriate.//如果未設定,則實現可以根據需要使用預設值。
	 */
	public void setConfigLocations(String... locations) {
		if (locations != null) {//非空
			Assert.noNullElements(locations, "Config locations must not be null");//斷言保證locations的每個元素都不為null
			this.configLocations = new String[locations.length];
			for (int i = 0; i < locations.length; i++) {
				this.configLocations[i] = resolvePath(locations[i]).trim();//去空格,很好奇resolvePath做了什麼事情?
			}
		}
		else {
			this.configLocations = null;
		}
	}
複製程式碼

進入resolvePath()方法看看:

	/**
	 * 解析給定的資源路徑,必要時用相應的環境屬性值替換佔位符,應用於資源路徑配置。
	 * Resolve the given path, replacing placeholders with corresponding
	 * environment property values if necessary. Applied to config locations.
	 * @param path the original file path
	 * @return the resolved file path
	 * @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)
	 */
	protected String resolvePath(String path) {
		return getEnvironment().resolveRequiredPlaceholders(path);
	}
複製程式碼

進入getEnvironment()看看:

	/**
	 * {@inheritDoc}
	 * <p>If {@code null}, a new environment will be initialized via
	 * {@link #createEnvironment()}.
	 */
	@Override
	public ConfigurableEnvironment getEnvironment() {
		if (this.environment == null) {
			this.environment = createEnvironment();
		}
		return this.environment;
	}
複製程式碼

進入createEnvironment(),方法,我們看到在這裡建立了一個新的StandardEnviroment物件,它是Environment的實現類,表示容器執行的環境,比如JDK環境,Servlet環境,Spring環境等等,每個環境都有自己的配置資料,如System.getProperties()System.getenv()等可以拿到JDK環境資料;ServletContext.getInitParameter()可以拿到Servlet環境配置資料等等,也就是說Spring抽象了一個Environment來表示環境配置。

Spring容器IOC初始化過程---今天終於進行總結了

生成的StandardEnviroment物件並沒有包含什麼內容,只是一個標準的環境,所有的屬性都是預設值。

總結:對傳入的path進行路徑解析

第八步:這一步是重頭戲

先做個小結:到現在為止,我們擁有了以下例項:

Spring容器IOC初始化過程---今天終於進行總結了

現在程式碼執行到如下圖的refresh()方法:

Spring容器IOC初始化過程---今天終於進行總結了

看一下這個方法的內容是什麼?

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 重新整理前準備工作,包括設定啟動時間,是否啟用標識位,初始化屬性源(property source)配置
			prepareRefresh();

			// 建立beanFactory(過程是根據xml為每個bean生成BeanDefinition並註冊到生成的beanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			//準備建立好的beanFactory(給beanFactory設定ClassLoader,設定SpEL表示式解析器,設定型別轉化器【能將xml String型別轉成相應物件】,
			//增加內建ApplicationContextAwareProcessor物件,忽略各種Aware物件,註冊各種內建的對賬物件【BeanFactory,ApplicationContext】等,
			//註冊AOP相關的一些東西,註冊環境相關的一些bean
			prepareBeanFactory(beanFactory);

			try {
				// 模板方法,為容器某些子類擴充套件功能所用(工廠後處理器)這裡可以參考BeanFactoryPostProcessor介面的postProcessBeanFactory方法
				postProcessBeanFactory(beanFactory);

				// 呼叫所有BeanFactoryPostProcessor註冊為Bean
				invokeBeanFactoryPostProcessors(beanFactory);

				// 註冊所有實現了BeanPostProcessor介面的Bean
				registerBeanPostProcessors(beanFactory);

				// 初始化MessageSource,和國際化相關
				initMessageSource();

				// 初始化容器事件傳播器
				initApplicationEventMulticaster();

				// 呼叫容器子類某些特殊Bean的初始化,模板方法
				onRefresh();

				// 為事件傳播器註冊監聽器
				registerListeners();

				// 初始化所有剩餘的bean(普通bean)
				finishBeanFactoryInitialization(beanFactory);

				// 初始化容器的生命週期事件處理器,併發布容器的生命週期事件
				finishRefresh();
			}
			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}
				// 銷燬已建立的bean
				destroyBeans();
				// 重置`active`標誌
				cancelRefresh(ex);
				throw ex;
			}
			finally {
                                //重置一些快取
				resetCommonCaches();
			}
		}
	}
複製程式碼

在這裡我想說一下,這個refresh()方法其實是一個模板方法,很多方法都讓不同的實現類去實現,但該類本身也實現了其中一些方法,並且這些已經實現的方法是不允許子類重寫的,比如:prepareRefresh()方法。更多模板方法設計模式,可看我之前的文章 談一談我對‘模板方法’設計模式的理解(Template)

先進入prepareRefresh()方法:

    /**
	 * Prepare this context for refreshing, setting its startup date and
	 * active flag as well as performing any initialization of property sources.
	 */
	protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();//設定容器啟動時間
		this.closed.set(false);//容器關閉標誌,是否關閉?
		this.active.set(true);//容器啟用標誌,是否啟用?
        
		if (logger.isInfoEnabled()) {//執行到這裡,控制檯就會列印當前容器的資訊
			logger.info("Refreshing " + this);
		}

		// 空方法,由子類覆蓋實現,初始化容器上下文中的property檔案
		initPropertySources();

		//驗證標記為必需的所有屬性均可解析,請參閱ConfigurablePropertyResolver#setRequiredProperties
		getEnvironment().validateRequiredProperties();

		//允許收集早期的ApplicationEvents,一旦多播器可用,即可釋出...
		this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
	}
複製程式碼

控制檯輸出:

三月 22, 2018 4:21:13 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
資訊: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@96532d6: startup date [Thu Mar 22 16:21:09 CST 2018]; root of context hierarchy
複製程式碼

第九步:

進入obtainFreshBeanFactory()方法:

	/**
	 * 告訴子類重新整理內部bean工廠(子類是指AbstractApplicationContext的子類,我們使用的是ClassPathXmlApplicationContext)
	 * Tell the subclass to refresh the internal bean factory.
	 */
	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
		refreshBeanFactory();//重新整理Bean工廠,如果已經存在Bean工廠,那就關閉並銷燬,再建立一個新的bean工廠
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();//獲取新建立的Bean工廠
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);//控制檯列印
		}
		return beanFactory;
	}
複製程式碼

進入refreshBeanFactory()方法:

	/**
	 * 該實現執行該上下文的基礎Bean工廠的實際重新整理,關閉以前的Bean工廠(如果有的話)以及為該上下文的生命週期的下一階段初始化新鮮的Bean工廠。
	 * This implementation performs an actual refresh of this context's underlying
	 * bean factory, shutting down the previous bean factory (if any) and
	 * initializing a fresh bean factory for the next phase of the context's lifecycle.
	 */
	@Override
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {//如果已有bean工廠
			destroyBeans();//銷燬
			closeBeanFactory();//關閉
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();//建立一個新的bean工廠
			beanFactory.setSerializationId(getId());//為序列化目的指定一個id,如果需要,可以將此BeanFactory從此id反序列化回BeanFactory物件。
			//定製容器,設定啟動引數(bean可覆蓋、迴圈引用),開啟註解自動裝配
			customizeBeanFactory(beanFactory);
			////將所有BeanDefinition載入beanFactory中,此處依舊是模板方法,具體由子類實現
			loadBeanDefinitions(beanFactory);
			//beanFactory同步賦值
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
複製程式碼

總結:這一步主要的工作就是判斷重新整理容器前是否已經有beanfactory存在,如果有,那麼就銷燬舊的beanfactory,那麼就銷燬掉並且建立一個新的beanfactory返回給容器,同時將xml檔案的BeanDefinition註冊到beanfactory中。如果不太清楚可以回過頭看看我們的第三節第5點內容

第十步:

進入第九步的loadBeanDefinitions(beanFactory)方法中去take a look:

	/**
	 * 使用XmlBeanDefinitionReader來載入beandefnition,之前說過使用reader機制載入Resource資源變為BeanDefinition物件
	 * Loads the bean definitions via an XmlBeanDefinitionReader.
	 * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
	 * @see #initBeanDefinitionReader
	 * @see #loadBeanDefinitions
	 */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// 建立XmlBeanDefinitionReader物件
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// 使用當前上下文Enviroment中的Resource配置beanDefinitionReader,因為beanDefinitionReader要將Resource解析成BeanDefinition嘛!
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		//初始化這個reader
		initBeanDefinitionReader(beanDefinitionReader);
		//將beandefinition註冊到工廠中(這一步就是將bean儲存到Map中)
		loadBeanDefinitions(beanDefinitionReader);
	}
複製程式碼

控制檯輸出:

三月 22, 2018 5:09:40 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
資訊: Loading XML bean definitions from class path resource [applicationContext.xml]
複製程式碼

第十一步:

進入prepareBeanFactory(beanFactory)方法:

//設定bean類載入器
//設定Spring語言表示式(SpEL)解析器
//掃描ApplicationContextAware bean
//註冊類載入期型別切面織入(AOP)LoadTimeWeaver
//為各種載入進入beanFactory的bean配置預設環境
複製程式碼

第十二步:

postProcessBeanFactory(beanFactory)方法:

postProcessBeanFactory同樣作為一個模板方法,由子類來提供具體的實現,子類可以有自己的特殊對BeanDefinition後處理方法,即子類可以在這對前面生成的BeanDefinition,即bean的後設資料再處理。比如修改某個beanid/name屬性、scope屬性、lazy-init屬性等。

第十三步:

invokeBeanFactoryPostProcessors(beanFactory)方法:

該方法呼叫所有的BeanFactoryPostProcessor,它是一個介面,實現了此介面的類需重寫postProcessBeanFactory()這個方法,可以看出該方法跟第十二步的方法是一樣的,只不過作為介面,更多的是提供給開發者來對生成的BeanDefinition做處理,由開發者提供處理邏輯。

第十四步:

其餘剩下的方法基本都是像初始化訊息處理源,初始化容器事件,註冊bean監聽器到事件傳播器上,最後完成容器重新整理。

五、總結

恭喜我,我終於寫完了,同樣也恭喜你,你也閱讀完了。

我很佩服我自己能花這麼長時間進行總結髮布,之所以要進行總結,那是因為小編還是贊同好記性不如爛筆頭的說法。

你不記,你過陣子就會忘記,你若記錄,你過陣子也會忘記!區別在於忘記了,可以回過頭在很短的時間內進行回憶,查漏補缺,減少學習成本。

再者,我認為我分析的還不是完美的,缺陷很多,因此我將我寫的所有文章釋出出來和大家探討交流,汕頭大學有校訓說得非常地好,那就是說之知識是用來共享的,因為共享了,知識才能承前啟後。

現在再梳理一下Spring初始化過程:

  • 1、首先初始化上下文,生成ClassPathXmlApplicationContext物件,在獲取resourcePatternResolver物件將xml解析成Resource物件。
  • 2、利用1生成的context、resource初始化工廠,並將resource解析成beandefinition,再將beandefinition註冊到beanfactory中。

朋友們,發現毛病,請評論告訴小編,一起交流一起交流!

相關文章