ApplicationContext容器
1.概述
ApplicationContext接口代表了一個Spring容器,它主要負責實例化、配置和組裝bean。ApplicationContext接口間接繼承了BeanFactory接口,相較于BeanFactory一些基本的容器功能,ApplicationContext接口是在BeanFactory接口基礎上進行了擴展,增加了國際化、事件廣播、獲取資源等一些新的功能。
2.ApplicationContext系列類圖
從以上類圖中可以看出,ApplicationContext接口的派生體系,是一個非常龐大的家族。
- FileSystemXmlApplicationContext:默認從文件系統中加載bean定義信息的ApplicationContext實現。
- ClassPathXmlApplicationContext:默認從ClassPath中加載bean定義信息的ApplicationContext實現。
- XmlWebApplicationContext:專門用于Web應用程序的ApplicationContext實現。SpringMVC 中默認使用的容器。
- AnnotationConfigApplicationContext:是一個基于注解配置類的ApplicationContext實現。SpringBoot 中默認使用的容器。
- AnnotationConfigServletWebServerApplicationContext:是SpringBoot一個基于注解配置類的servlet web應用程序的ApplicationContext實現。容器中會一個內置的servlet 服務器。
- AnnotationConfigReactiveWebServerApplicationContext:是SpringBoot一個基于基于注解配置類的reactive web應用程序的ApplicationContext實現。容器中會一個內置的reactive 服務器。
3.refresh()方法
3.1概述
refresh方法是Spring容器中一個非常核心的方法。經過refresh方法后,一個完整的Ioc容器已經創建完成。refresh方法是在ConfigurableApplicationContext接口定義的,而給出具體這個方法實現的是在AbstractApplicationContext的類中。
從refresh方法的源碼可以發現,refresh方法中調用了12個子方法。這12個子方法其實就是Spring創建Ioc容器的12個步驟。
refresh方法其實是使用了模版方法模式。模板方法模式定義一個操作中算法的框架,而將一些步驟延遲到子類中。模板方法模式使得子類可以在不改變一個算法結構的情況下,重定義該算法的某些特定步驟。
refresh方法中調用的12個步驟方法,為Ioc容器的創建定義了一個總體框架。在各種具體的ApplicationContext的子類中,會根據自身具體的特性,再對這12個步驟方法進行重寫,但是創建容器的總體步驟是不變的。
3.2源碼
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//準備刷新上下文環境:設置其啟動日期和活動標志,初始化上下文環境中的占位符屬性源
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
//實例化DefaultListableBeanFactory實例并返回,對BeanFactory進行定制,加載BeanDefinition的信息
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.
//在容器初始化后,預留在子類中的上下文中,可以對容器進行修改
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
// 執行注冊在容器中的各種BeanFactory的處理器
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
//注冊Bean的各種處理器。處理器會在創建bean時調用。
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
//初始化上下文的Message源。如:i18n國際化處理
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.
// 查看ApplicationListener類型的bean,并注冊他們。
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
// 對上下文環境中剩余的單例bean完成初始化(非惰性的bean)
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
// 調用LifecycleProcessor生命周期處理器的onRefresh方法并發布ContextRefreshedEvent通知,
// 來完成上下文的刷新過程。
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.
//銷毀上下文容器中所有緩存的單例bean,已避免占用資源。
destroyBeans(); // Reset 'active' flag.
//取消此上下文的刷新嘗試,并重置active標志。
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();
}
}
}
3.2.1prepareRefresh
prepareRefresh方法主要是對上下文的刷新做了一些準備工作,該方法中主要做了以下幾件事情。
- 激活開關
prepareRefresh方法的激活開關,主要是程序中記錄了當前的時間戳,打開了當前上下文是否處于活動狀態的標志(設置為true) ,關閉當前上下文是否處于關閉狀態的標志(設置為false)。
- 初始化占位符
initPropertySources方法是用于初始化上下文環境中的占位符屬性。但是在AbstractApplicationContext類中,initPropertySources方法只是一個空方法,沒有任何實現代碼。該方法是專門預留給子類擴展實現用的。
我們可以自己實現一個繼承至AbstractApplicationContext的子類,并在子類的initPropertySources方法中,根據自己的需求來設置需要驗證的占位符。
- 對占位符屬性進行驗證
Spring對占位符屬性進行驗證時,先獲取ConfigurableEnvironment類型的實例,然后再調用這個實例的validateRequiredProperties方法來進行驗證。
在validateRequiredProperties方法中,會判斷占位符的屬性值是否為空,若值為空,就會拋出異常。默認情況下,一般是對系統中的環境變量和JVM的環境變量來進行判斷。
3.2.2obtainFreshBeanFactory
obtainFreshBeanFactory方法從方法名上,顧名思義就是獲取BeanFactory容器。Spring經過這個函數之后,ApplicationContext就已經擁有了BeanFactory 的全部功能。obtainFreshBeanFactory方法很簡單,它主要調用了兩個方法,這兩個方法分別是refreshBeanFactory和getBeanFactory方法。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //創建BeanFactory容器實例 refreshBeanFactory(); //返回創建的BeanFactory容器實例 return getBeanFactory();}
refreshBeanFactory方法
refreshBeanFactory方法的作用主要是創建BeanFactory容器的實例。refreshBeanFactory方法在AbstractApplicationContext類中只是一個虛方法,沒有給出具體的實現。這個方法的具體實現是在子類AbstractRefreshableApplicationContext類中給出的。具體源碼如下所示:
protected final void refreshBeanFactory() throws BeansException { //判斷是否已經創建了BeanFactory容器 //如果已經創建,則銷毀容器中的bean,并關閉容器 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //使用new方式創建一個DefaultListableBeanFactory的容器 DefaultListableBeanFactory beanFactory = createBeanFactory(); //給容器設置一個序列化的id beanFactory.setSerializationId(getId()); //自定義DefaultListableBeanFactory容器的相關屬性 customizeBeanFactory(beanFactory); //給容器加載BeanDefinition loadBeanDefinitions(beanFactory); this.beanFactory = beanFactory; } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); }}
通過以上源碼可以發現refreshBeanFactory方法主要做了以下幾件事情:
- 創建DefaultListableBeanFactory容器
對于DefaultListableBeanFactory容器的創建, Spring中直接是new的方式創建了一個DefaultListableBeanFactory的實例,非常簡單。DefaultListableBeanFactory是Bean工廠的一個默認實現,它提供了容器的基本功能。
- 給容器指定一個序列化的id
- 自定義容器的相關屬性
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { if (this.allowBeanDefinitionOverriding != null) { beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.allowCircularReferences != null) { beanFactory.setAllowCircularReferences(this.allowCircularReferences); }}
customizeBeanFactory方法用于自定義容器的相關屬性。方法中判斷了allowBeanDefinitionOverriding和allowCircularReferences這兩個屬性的值是否為空,不為空的話,就對容器進行屬性值設置。
allowBeanDefinitionOverriding屬性表示是否允許覆蓋同名的BeanDefinition,默認值是true。
allowCircularReferences屬性表示是否允許bean之間能否進行循環依賴,默認值也是true。
- 給容器加載BeanDefinition
BeanDefinition定義了描述Bean的元數據信息。BeanDefinition的加載,其實就是把Spring外部對Bean的定義信息轉化成IoC容器中內部數據結構的過程。
IoC容器對Bean的管理和依賴注入功能的實現,其實都是通過對其持有的BeanDefinition進行各種相關操作來完成的。
Spring外部對BeanDefinition的定義是多種形式的,BeanDefinition的定義有xml文件,properties文件和注解等形式。對于這三種形式的BeanDefinition,Spring分別提供了XmlBeanDefinitionReader,PropertiesBeanDefinitionReader,AnnotatedBeanDefinitionReader三個類來加載相應的BeanDefinition信息。
getBeanFactory方法
getBeanFactory方法的作用主要是返回已經創建的BeanFactory容器實例,比較簡單,無需展開。
3.2.3prepareBeanFactory
prepareBeanFactory方法主要是對beanFactory容器的特性進行一些配置的準備工作。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { // Tell the internal bean factory to use the context's class loader etc. //設置容器的ClassLoader beanFactory.setBeanClassLoader(getClassLoader()); //設置容器的SpEL語言解析器,增加對SpEL語言的支持。 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); //添加容器的屬性編輯器 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); // Configure the bean factory with context callbacks. //為容器注冊ApplicationContextAwareProcessor的后置處理器 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); //設置容器忽略自動裝配的接口 beanFactory.ignoreDependencyInterface(EnvironmentAware.class); beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class); beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class); beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class); beanFactory.ignoreDependencyInterface(MessageSourceAware.class); beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // BeanFactory interface not registered as resolvable type in a plain factory. // MessageSource registered (and found for autowiring) as a bean. //對容器注冊自動裝配所依賴特殊類型 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory); beanFactory.registerResolvableDependency(ResourceLoader.class, this); beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this); beanFactory.registerRe