IoC容器的初始化过程
在上一篇中我们讨论了对代表BeanDefinition的Resource定位的分析后,接下来就需要了解整个BeanDefinition信息的载入过程。
在AbstractApplicationContext#refresh()方法中,有这样一段代码
ConfiguableListableBeanFactory beanFactory = obtainFreshBeanFactory();
这个方法的作用就是通知子类重新启动BeanFactory,源码中是这样说的
Tell the subclass to refresh the internal bean factory.
那么接下来就让我们看看这个方法内部是如何实现的
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
在当前方法中调用了refreshBeanFactory(),从方法名上可以看到,这个才是真正重新启动BeanFactory的方法。接下来会调用getBeanFactory()返回我们已经重新启动好的BeanFactory()。当然在AbstractApplicationContext中是没有实现这两个方法的,他们的具体实现我们需要到AbstractApplicationContext的子类中寻找。
AbstractApplicationContext的子类AbstractRefreshableApplicationContext实现了refreshBeanFactory()和getBeanFactory()方法。
1.refreshBeanFactory()
/**
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.
这个实现了一个对当前上下文中的beanFactory刷新的工作,关闭以前的BeanFactory和为上下文的下一阶段初始化一个新的BeanFactory
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果当前上下文已经持有一个还未关闭BeanFactory,那么首先销毁Bean,然后关闭BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建出一个新的BeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
//将当前上下文的id作为BeanFactory的序列化id
beanFactory.setSerializationId(getId());
//自定义当前上下文的内部beanFactory
customizeBeanFactory(beanFactory);
//加载配置文件中的BeanDefinition
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
通过以上简单的分析,我们可以知道,AbstractRefreshableApplicationContext#refreshBeanFactory()的作用是对当前ApplicationContext中的BeanFactory进行重启,重新加载BeanFactory。
到这里就是我们今天的重点了,BeanDefinition的载入
loadBeanDefinition(DefaultListableBeanFactory beanFactor),通常通过委托给一个或多个BeanDefinitionReader,将BeanDefinition加载到给定的BeanFactory中。今天我们主要看一下AbstractXmlApplicationContext中实现的loadBeanDefintions方法
2. AbstractXMLApplicationContext#loadBeanDefinition(DefaultListableBeanFactory beanFactory)
/**
Loads the bean definitions via an XmlBeanDefinitionReader.
通过XmlBeanDefinitionReader来加载bean的定义
@see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
@see #initBeanDefinitionReader
@see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//给给定的BeanFactory创建一个新的XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
//给当前上下文环境配置bean定义读取器
// resource loading environment.
//资源加载环境
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
//允许子类提供自定义个的bean定义读取器
// then proceed with actually loading the bean definitions.
//然后继续加载Bean定义
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
/**
* Load the bean definitions with the given XmlBeanDefinitionReader.
在给定的XmlBeanDefintionReader中加载bean定义
<p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
method; hence this method is just supposed to load and/or register bean definitions.
bean工厂的生命周期由refreshBeanFactory方法处理,因此这个方法只进行bean定义的加载和/或注册。
@param reader the XmlBeanDefinitionReader to use
要使用到的读取器
@throws BeansException in case of bean registration errors
@throws IOException if the required XML document isn't found
@see #refreshBeanFactory
@see #getConfigLocations
@see #getResources
@see #getResourcePatternResolver
*/
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//使用Resource的方式来获取文件资源位置
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//使用String的形式获得配置文件的位置
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
以上两个方法是在XMLApplicationContext中对Bean定义载入工作的准备,真正进行载入工作的还是BeanDefinitionReader,由于我们当前是XMLApplicationContext,所以使用的是XmlBeanDefinitionReader。这个读取器继承了AbstractBeanDefinitionReader,在上述代码中使用了两种不同的形式获取配置文件的位置,一种是直接传一个Resource对线,还有一种是传一个String,使用String这种形式,它是在AbstractBeanDefinitionReader中有所实现,最后还是会通过传过来的String,获取到Resource来调用子类实现的loadBeanDefinitions(Resource resource)方法,所以在这里我们就直接看一下子类中的loadBeanDefinitions(Resource resource)是怎么实现的。
- XmlBeanDefinitionReader#loadBeanDefinitionReader
/**
Load bean definitions from the specified XML file.
从指定的XMl文件中加载bean 定义
@param resource the resource descriptor for the XML file
@return the number of bean definitions found
@throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
/**
Load bean definitions from the specified XML file.
@param encodedResource the resource descriptor for the XML file,
allowing to specify an encoding to use for parsing the file
允许指定一个解析文件的编码
@return the number of bean definitions found
@throws BeanDefinitionStoreException in case of loading or parsing errors
*/
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
//断言,资源必须不为null
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
//这里有一个同步缓存,使用ThreadLoacl,在没有取到的情况在,将新的EncodeResource存到ThreadLocal中
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
//接下来就是通过输入流读取文件中的配置了,然后调用doLoadBeanFactory()方法
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
同学们可能注意到了,上面的方法中使用到了ThreadLocal,它会为每个线程提供独立的变量副本,这就说明我们loadBeanDefinition这个方法是一个并发方法,可能会同时加载多个资源文件。
4.XmlBeanDefinitionReader#doLoadBeanDefinition(InputSource source, Resource resource)
/**
* Actually load bean definitions from the specified XML file.
真正从指定XMl文件加载bean定义
* @param inputSource the SAX InputSource to read from
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
* @see #doLoadDocument
* @see #registerBeanDefinitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//取得XMl文件中的document对象
Document doc = doLoadDocument(inputSource, resource);
//将document对象解析并转化为容器内部数据结果
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
·····
}
根据源码,我们可以知道BeanDefinition的载入分为两个部分,第一部分是通过XML的解析器得到对应的document对象,但是这些document并不没有按照Spring的Bean规则进行解析。完成通用XMl解析之后,我们会通过documentReader对document进行解析。
5.XmlBeanDefinitionReader#registerBeanDefinition(Document doc,Resource resource)
/**
* Register the bean definitions contained in the given DOM document.
在给定的DOM deocument中注册包含的bean定义
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
创建解析器的实例并调用
* {@code registerBeanDefinitions} on it.
* @param doc the DOM document
* @param resource the resource descriptor (for context information)
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of parsing errors
* @see #loadBeanDefinitions
* @see #setDocumentReaderClass
* @see BeanDefinitionDocumentReader#registerBeanDefinitions
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//创建一个新的document解析器
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//
int countBefore = getRegistry().getBeanDefinitionCount();
//调用解析器的registerBeanDfinitions
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
由于在BeanDefinitionDocumentReader中的registerBeanDefinitions是一个接口方法,所以我们就需要看一下他的实现类中是如何实现这个方法的。
6. DefaultBeanDefinitionDocumnetReader#registerBeanDefinitions()
/**
* This implementation parses bean definitions according to the "spring-beans" XSD
这个实现根据XSD解析bean定义
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
/**
* Register each bean definition within the given root {@code <beans/>} element.
在给定的根元素<bean>中注册每一个bean定义
*/
@SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
在完成了一系列的载入和解析工作之后,在这些动作完成以后,用户定义的BeanDefinition信息都已经在Ioc容器中建立起自己的数据结果和相应的数据表示,接下来就是对其进行注册了。