前文回顾
之前讲了探索SpringBoot-一起看看Spring核心源码之ApplicationContext(八),最后提到了ApplicationContext
的refresh
的三个核心过程分别是Bean发现、读取、注册。今天来进一步分析下源码。
refresh之obtainFreshBeanFactory
我们直接定位到AbstractApplication
的refresh
函数中的下面这一行。有想要看之前的解析过程的看这篇探索SpringBoot-一起看看Spring核心源码之ApplicationContext(八)。
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
看注释可以发现,这个方式是告诉子类去refresh
内部bean factory
,
我们再进入到obtainFreshBeanFactory
里面。
/**
* 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
即可。
/**
* 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)
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
里面的实现。这里使用到了模板模式,有不懂模板设计模式的同学,之后我会单独再补习一下。
@Override
protected Resource[] getConfigResources() {
return this.configResources;
}
至此,可以认为是refresh
的Bean
的发现过程。
明天再分析下,Bean
的读取和注册。
关于写作
以后这里每天都会写一篇文章,题材不限,内容不限,字数不限。尽量把自己每天的思考都放入其中。
如果这篇文章给你带来了帮助,能请你写下是哪个部分吗?有效的反馈是对我最大的帮助。
我是shane。今天是2019年8月14日。百天写作计划的第二十一天,21/100。