文章目錄
概要
以前總是很忙,沒有時間寫博客,現在擠出時間寫一些總結性及實用性的博客。
首先聊一下,之所以開始看源碼,是因爲隨着自身對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的實例化