ApplicationContext
是Spring框架中最基礎的接口之一,可以認爲其實現類就是一個Spring的環境(容器),而一個簡單的Spring應用的啓動過程就是一個ApplicationContext
的實現類的實例化過程,是研究Spring源碼的很好的切入點。這裏研究的實現類是AnnotationConfigApplicationContext
,運行的代碼爲前文Spring探祕0:源碼構建中的測試代碼,啓動類的代碼如下:
public class DemoApp {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
DemoComponent component = context.getBean(DemoComponent.class);
component.foo();
}
}
要區分的一個概念就是“容器”,本文中提到的容器都是指Spring框架的容器,即管理Service,Dao以及Component這些Bean的一個“環境”。與SpingMVC的容器不同,SpringMVC的容器是管理Controller的環境。與Tomcat這些Web容器就更不一樣了。
相關的類與接口
繼承結構
AnnotationConfigApplicationContext
這個類從類名就可以看出,它是一個通過註解來配置的Spring容器,實例化該容器時,可以傳入一個配置類,上面代碼中的AppConfig
就是我自定義的一個配置類,其中唯一的配置項就是通過@ComponentScan
註解配置了掃描的包。
AnnotationConfigApplicationContext
這個類的繼承結構如下圖(圖中只包含了與啓動容器關係較大的類和接口,實際的繼承層次要複雜得多):
從中可以看出
ApplicationContext
最終還是要實現BeanFactory
,而這個父接口的含義可以參考前一篇文章。因此可以說ApplicationContext
是BeanFactory
功能上的增強。AnnotationConfigApplicationContext
的直接父類GenericApplicationContext
其實還有其他的一些子類,如GenericXmlApplicationContext
等,分別用於表示不同配置方式的容器。
啓動容器時涉及到的類
容器的啓動過程就是一個ApplicationContext
的實例化過程,本文研究的容器實現類爲AnnotationConfigApplicationContext
,啓動的過程中會涉及到許多其他的類,包括容器中的實例對象的實例化,向容器中註冊的一些初始的處理器等。這裏通過類圖的形式總結了部分類以及這些類之間的關係。
各類的簡介
自頂向下地簡單介紹相關的類:
BeanDefinition
: 用於描述一個Bean實例的信息;根據Bean的來源不同有具體的實現類,如AnnotatedGenericBeanDefinition
,RootBeanDefinition
,ScannedGenericBeanDefinition
等。AbstractApplicationContext
: 是ApplicationContetxt
接口的抽象實現類,該類沒有指定容器的配置類型,只實現了容器的一些通用功能,這些功能增強了簡單的BeanFactory
。與簡單的BeanFactory
相比,ApplicationContext
應該能夠主動探測到容器內的一些特殊的bean。因此該類自動註冊了一些BeanFactoryPostProcessor
,BeanPostProcessor
,ApplicationListener
,在一些特殊的時期能夠自動調用這些對象的功能。BeanFactoryPostProcessor
: 這就是一種ApplicationContext
能夠主動調用的特殊的Bean,從類名不難猜到,實現了該接口的Bean會在Bean工廠初始化完成後被調用(稱爲後置處理器)。因此我們可以通過實現該接口來插手Bean工廠初始化過程,從而擴展Spring的功能。值得注意的是,該接口的實現類只能去處理、修改bean definitions,而不應該對Bean實例作任何處理,因爲調用時,bean都還沒實例化。BeanDefinitionRegistryPostProcessor
,它是BeanFactoryPostProcessor
的子接口,即除了實現BeanFactoryPostProcessor
的功能之外,還能夠實現動態地向容器中添加BeanDefinition
的功能。該功能會在一般的BeanFactoryPostProcessor
之前被調用。PostProcessorRegistrationDelegate
:AbstractApplicationContext
將調用後置處理器的功能委派給該類,應該只是爲了減少每個類的代碼量?BeanDefinitionRegistry
:registry意爲“註冊處”,所以這個接口也就是表示BeanDefinition
註冊的地方,一般都是由Bean工廠實現,用於管理BeanDefinition
,包括註冊、移除、獲取等。對BeanDefinition
有管理需要的地方就可以調用該接口的方法。GenericApplicationContext
: 是AbstractApplicationContext
的子類,主要維護了一個DefaultListableBeanFactory
實例,通過該實例實現了BeanFactory
接口(裝飾者模式)。另外也實現了BeanDefinitionRegistry
接口,給有需要的類註冊BeanDefinition
。DefaultListableBeanFactory
:這就是Spring內BeanFactory
接口和BeanDefinitionRegistry
的一個默認實現。AnnotationConfigApplicationContext
: 是GenericApplicationContext
的子類,是一個獨立的應用上下文(容器),構造時可以接收多個組件類作爲參數(一般是配置類)。ClassPathBeanDefinitionScanner
: 是AnnotationConfigApplicationContext
中一個重要的工具,用於掃描classpath上所有的Bean,並將相應的BeanDefifnition
註冊到給定的BeanDefinitionRegistry
中。(一般registry就是一個BeanFactory
或者是ApplicationContext
,即當前的Spring容器)AnnotatedBeanDefinitionReader
:是AnnotationConfigApplicationContext
中另一個重要的工具,也是用於接收一個Bean類,轉換爲BeanDefifnition
並將其註冊到registry中,但是不同點在於它只用於讀取顯式註冊的類,即與ClassPathBeanDefinitionScanner
相比沒有掃描classpath的功能。
啓動流程原理分析
AnnotationConfigApplicationContext
的構造方法的調用層次如下圖所示:
構造父類GenericApplicationContext
- 再遞歸地構造父類
AbstractApplicationContext
,該父類中的BeanFactoryPostProcessor
的list就是在此時實例化的 - 實例化beanFactory屬性爲DefaultListableBeanFactory。
工廠模式:
AbstractApplicationContext
類定義了一個抽象方法getBeanFactory()
,通過該方法實現了BeanFactory
接口。該方法就是需要子類去實現的工廠方法,不同的子類可能有不同的BeanFactory
實現。
實例化reader屬性
-
設置reader中的registry,conditionEvaluator屬性
-
調用
AnnotationConfigUtils.registerAnnotationConfigProcessors()
向容器註冊一些後置處理器的BeanDefinition
(並且其實現類均爲RootBeanDefinition
),包括:ConfigurationClassPostProcessor
:是一個BeanDefinitionRegistryPostProcessor
實現類,用於處理配置類AutowiredAnnotationBeanPostProcessor
:用於處理自動注入的類CommonAnnotationBeanPostProcessor
:支持JSR-250的註解PersistenceAnnotationBeanPostProcessor
:支持JPA的註解EventListenerMethodProcessor
DefaultEventListenerFactory
-
此時,reader的實例化已經完成,程序運行到這裏(指
AnnotatedBeanDefinitionReader
構造方法的最後一行)時,Bean工廠中應該已經存在一些BeanDefinition
:
實例化scanner屬性
- 該實例化過程比較簡單,只是在scanner的掃描過濾器中添加了
@Component
這個註解。
註冊構造方法的參數傳入的組件類
- 調用
reader.register()
,追蹤調用,最終的邏輯是在doRegisterBean()
方法實現的
刷新 refresh()
- 刷新容器是容器啓動的最重要的一個階段,有很多的工作包括準備Bean工廠、調用
BeanFactoryPostProcessors
、註冊BeanPostProcessors
等等,需要另外進行詳細的分析。