這篇來看下AbstractApplicationContext中的refresh()方法,這個方法初始化且啟動整個spring容器的核心。方法名字了解起來就是重新整理的意思,意味着重新整理整個Spring容器,做好一切準備。
public void refresh() throws BeansException, IllegalStateException {
//初始化容器需要加鎖,防止并發加載,startupShutdownMonitor鎖對象
synchronized (this.startupShutdownMonitor) {
// 預加載重新整理,裡面包含初始化準備工作
prepareRefresh();
// 建立bean工廠,加載初始化beanDefinitionMap、依賴關系、注入屬性等等對象,注入對象解析的實作在裡面
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 準備beanFactory綁定到context容器
prepareBeanFactory(beanFactory);
try {
// 允許在context子類中對bean工廠進行後處理
postProcessBeanFactory(beanFactory);
// 執行個體化并調用所有已注冊的beanfactorypostprocessor bean對象
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊攔截bean建立的bean處理器.
registerBeanPostProcessors(beanFactory);
// 初始化國際化消息資源
initMessageSource();
// 初始化應用事件廣播器,用來觸發事件監聽
initApplicationEventMulticaster();
// 初始化context子類的特殊bean對象
onRefresh();
// 注冊監聽器和事件類型,就是把所有對應的監聽器和事件類型儲存到應用容器裡
registerListeners();
// 執行個體化所有剩下的(非惰性初始化)單例.
finishBeanFactoryInitialization(beanFactory);
// 最後一步:釋出重新整理完畢相應的事件ContextRefreshedEvent
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 出現重新整理異常,則銷毀已經建立的單例以避免占用資源。
destroyBeans();
// 重置激活标志
cancelRefresh(ex);
// 抛出異常
throw ex;
}
finally {
// 清楚部分加載解析用到的beanmeta緩存,因為我們已經在spring核心容器裡重置了常見的内部緩存 可能不再需要單身bean的中繼資料了
resetCommonCaches();
}
}
}
咋一看這個方法的代碼很少,其實裡面調用很多層架構所需要的實作,(初略總結)大概做了如下事情:
加載PropertySources、環境參數、初始化并建構BeanFactory、解析注冊XmlBeanDefinition,初始化建立裝載bean、解析Bean之間的依賴關系、國際化消息初始化、初始化監聽事件廣播、注冊所有監聽器、事件釋出通知結束等。
預加載方法:
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();//記錄啟動時間
this.closed.set(false);//容器沒有關閉辨別
this.active.set(true);//容器啟動激活辨別
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// 初始化加載系統上下文環境屬性參數資源,在ContextLoader可能已加載
initPropertySources();
// 校驗所有的設定Required的Properties,在ConfigurablePropertyResolver#setRequiredProperties設定
getEnvironment().validateRequiredProperties();
// 初始化容器早期事件Set集合
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
本章重點分析BeanFactory初始化過程:
- 預準備 初始化環境property sources
- 初始化BeanFactory,解析xml注冊配置和bean對象
- 建立并重新整理BeanFactory,解析注冊Bean
AbstractRefreshableApplicationContext#refreshBeanFactory方法實作
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();//建立BeanFactory對象
beanFactory.setSerializationId(getId());//序列化id
customizeBeanFactory(beanFactory);//
loadBeanDefinitions(beanFactory);//加載資源解析BeanDefinition
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
可以看到refreshBeanFactory中的方法loadBeanDefinitions加載資源并去解析xml,然後建立BeanDefinition等操作
XmlWebApplicationContext的具體實作loadBeanDefinitions方法,第一個參數location就是web.xml配置的configLocations路徑,第二參數actualResources的資源Set
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}