以前講了探索SpringBoot-一塊兒看看Spring核心源碼之ApplicationContext(八),最後提到了ApplicationContext
的refresh
的三個核心過程分別是Bean發現、讀取、註冊。今天來進一步分析下源碼。java
咱們直接定位到AbstractApplication
的refresh
函數中的下面這一行。有想要看以前的解析過程的看這篇探索SpringBoot-一塊兒看看Spring核心源碼之ApplicationContext(八)。spring
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
複製代碼
看註釋能夠發現,這個方式是告訴子類去refresh
內部bean factory
,設計模式
咱們再進入到obtainFreshBeanFactory
裏面。ide
/** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
複製代碼
能夠看到,返回值beanFactory
是由getBeanFacotry
來獲取的。可是,以前進行過一個函數調用,refreshBeanFactory
,這個又是幹什麼的呢?按照以前說法,refresh
函數會先通過Bean
的發現,讀取和註冊的過程,其實這個refreshBeanFactory
就是進行Bean
的發現,讀取和註冊實現。讓咱們繼續往下看。函數
/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */
//大意爲對這個context指定的bean factory執行特定的refresh操做。
//若是以前有bean factory則銷燬,而後爲了下一個context的生命週期中初始化一個新的bean factory
@Override
protected final void refreshBeanFactory() throws BeansException {
//判斷是否存在BeanFactory
if (hasBeanFactory()) {
//銷燬Beans
destroyBeans();
closeBeanFactory();
}
try {
//建立一個BeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
//設置序列號,方便反序列化
beanFactory.setSerializationId(getId());
//定製BeanFacotory
customizeBeanFactory(beanFactory);
//發現BeanDefinition
loadBeanDefinitions(beanFactory);
//將beanFactoryMonitor設置爲建立的beanFactory
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
複製代碼
一切都在註釋中了,先認真看一遍註釋。能夠發現,這裏最關鍵的Bean發現的過程是在loadBeanDefinitions
中。嘗試查看實現,發現這個方法是一個抽象方法,具體實現類會根據不一樣的來源來實現不一樣的方式。咱們是XML
的方式,直接看AbstractXmlApplicationContext
便可。post
/** * Loads the bean definitions via an XmlBeanDefinitionReader. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader * @see #loadBeanDefinitions */
//大意爲用XmlBeanDefinitionReader來加載bean definitions
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//用給定的BeanFactory來建立一個XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//用這個上下文的資源加載環境來配置bean definition reader
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//容許子類對Reader提供特定的初始化,而後執行實際的加載Bean definitions
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
複製代碼
看註釋,而後繼續看loadBeanDefinitions(beanDefinitionReader)this
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//注意這裏調用了getConfigResources()方法
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
複製代碼
注意這裏調用了getConfigResources()
方法,咱們在回頭看一下ClassPathApplicationContext
裏面的實現。沒錯,這裏AbstractXmlApplicationContext
其實最終調用的是ClassPathApplicationContext
裏面的實現。這裏使用到了模板模式,有不懂模板設計模式的同窗,以後我會單獨再補習一下。spa
@Override
protected Resource[] getConfigResources() {
return this.configResources;
}
複製代碼
至此,能夠認爲是refresh
的Bean
的發現過程。debug
明天再分析下,Bean
的讀取和註冊。設計
之後這裏天天都會寫一篇文章,題材不限,內容不限,字數不限。儘可能把本身天天的思考都放入其中。
若是這篇文章給你帶來了幫助,能請你寫下是哪一個部分嗎?有效的反饋是對我最大的幫助。
我是shane。今天是2019年8月14日。百天寫做計劃的第二十一天,21/100。