1、spring源碼解析之概況流程

概要

​ 以前總是很忙,沒有時間寫博客,現在擠出時間寫一些總結性及實用性的博客。

​ 首先聊一下,之所以開始看源碼,是因爲隨着自身對java的理解不斷加深,感覺自己對技術架構方面很感興趣。所以想深入瞭解一下開源框架的本質,看各種開源框架、jdk源碼有一段時間了,也在源碼上寫了很多自己理解的註釋,一直沒有整體總結,偶爾想起來有一些思路,但是容易忘記,所以想做一些總結,每一個我覺得非常複雜的核心流程,我都會用一張或時許圖、流程圖來進行學習總結。

​ 總的來說,spring的總體流程不是很複雜,但是細思極恐;每一個類、每一個方法的內部實現能牽扯出很多與之相關聯的類;其中實現錯綜複雜,不過不管怎麼難,帶着設計模式的思想、以及多思考,多實踐,多總結的角度,多看幾遍,終歸會有所收穫(可以自己手繪一些uml時序圖、流程圖來加深理解)。

​ 本文及後續文章主要基於spring 5.0源碼分析spring 核心的 ioc和aop,首先縷清spring運行的整體脈絡流程。由於註解驅動(無xml)的流行,本文不會涉及xml的上下文及容器刷新流程(注意:作者之前有使用過ssm,涉及過xml配置,個人覺得xml配置會讓人思想上更容易理解spring容器、bean的定義等等,感覺現在直接使用spring boot的人可能漸漸忽略了spring纔是基石)。

1.程序入口:加載配置類

​ 本文主要採用AnnotationConfigApplicationContext 作爲程序的起點。

public class MyApplication {
	public static void main(String[] args) {
		//spring上下文,啓動一個spring應用程序。
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApp.class);
		Car bean =(Car) context.getBean("car");
		System.out.println(bean.getColor()+bean.getPrice());
	}
}

2.註解配置上下文AnnotationConfigApplicationContext

AnnotationConfigApplicationContext:註解配置上下文。

	public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
		//this()由於繼承關係,所以會先調用父類的構造方法,在調用子類的構造方法
		/**
		 * @Author coyhzx
		 * @Description 類繼承結構(暫不涉及接口)DefaultResourceLoader -> AbstractApplicationContext ->GenericApplicationContext -> AnnotationConfigApplicationContext
		 *   {@link DefaultResourceLoader} 初始化當前線程的classLoader
		 *   {@link AbstractApplicationContext} 初始化資源模式匹配解析器 PathMatchingResourcePatternResolver
		 *   {@link GenericApplicationContext}  初始化 ioc底層容器 DefaultListableBeanFactory
		 *   {@link AnnotationConfigApplicationContext}  初始化 reader 註解的bean的讀取器,scanner 類路徑的bean的掃描器
		 **/
		this();
		//componentClasses 註冊單個組件bean
		register(componentClasses);
		//刷新容器
		refresh();
	} 


AnnotationConfigApplicationContext的無參構造this();

	public AnnotationConfigApplicationContext() {
		//創建一個註解的bean的讀取器,猜想一下,很關鍵,要想讀取註解,是不是要註冊一些內置的BeanPostProcess後置處理器來處理註解bean。
		this.reader = new AnnotatedBeanDefinitionReader(this);
		//創建一個類路徑的bean的掃描器
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

3.刷新上下文

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.
			// 獲取beanFactory
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			// beanFactory的一些準備工作
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				// 留給子類Context去實現,目前爲空
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 調用 beanFactoryPostProcessors beanFactory的後置處理器
				//重點: 解析入口的配置類
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				//註冊Bean的後置處理器
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				//初始化消息源
				initMessageSource();

				// Initialize event multicaster for this context.
				//初始化事件廣播監聽器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				//子類初始化一些特殊的bean
				onRefresh();

				// Check for listener beans and register them.
				// 檢查並註冊監聽器
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				//重點:完成所有非延遲加載bean的初始化
				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();
			}
		}
	}

​ 簡單來說:整體流程其實已經走完了,感覺有點走馬觀花,但是其中每一個方法每一個new對象,每一個父子繼承的實現其實很複雜。得慢慢梳理。先看一下AnnotationConfigApplicationContext得類圖,從圖中可以看成,spirng把一個上下文的接口抽象出來了很多頂級接口,和抽象類和實現類,看多了很多源碼,摸清楚了一些spring框架的一些小特點,通常spring接口會有頂級接口,定義規範,抽象類實現接口的公用方法,具體的特性交給不同實現類去實現,這裏着重介紹一些核心得關鍵接口和類。

4.核心類AnnotationConfigApplicationContext的類圖結構

在這裏插入圖片描述

BeanFactory: Bean工廠的頂級接口。裏面定義了變量factorbean的前綴&,getBean()、getType()、是否單例、是否原型的方法等等。

ResourceLoader: 資源加載的頂級接口。很簡單,裏面僅僅定義了classpath的url,getClassLoader()、getResource(String location)方法。
DefaultResourceLoader:默認的資源加載類。加載classpath下的資源文件。

AbstractApplicationContext:抽象的上下文類。實現了可配置的上下文接口ConfigurableApplicationContext,是一個很重要的類,refresh()刷新容器的主要邏輯就在其中。

GenericApplicationContext:廣泛普通的上下文類。我們真正底層的ioc容器,bean工廠DefaultListableBeanFactory就是在它的初始化中創建的。

AnnotationConfigApplicationContex:註解配置的上下文類。很關鍵,是可以直接定位加載註解配置類的上下文。

BeanDefinitionRegistry:bean的註冊接口。定義了註冊bean、移除bean等接口。

5.跟蹤源碼進入ioc

​ 先簡單的介紹AnnotationConfigApplicationContext#register(componentClasses)方法,以及跟蹤源碼,可以直接進入我們常說的ioc容器之中,看着上下文是如何幫我們在上下文中創建一個BeanDefinition,並放入容器中。

​ 好了,儘管我省略了很多細節,但是整體的配置類BeanDefinition註冊流程通過下圖展示,spring在ioc容器中註冊了bean的定義了,那麼如何去完成實例化bean的實例呢,請期待我下一篇文章把!
在這裏插入圖片描述
下一篇:2、spring源碼解析之單例bean的實例化

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章