IoC容器的初始化包括​​BeanDefinition​​​的Resource定位、载入和注册这三个基本的过程。从[读书笔记]Spring中的容器设计一文可知IOC容器主要有两个体现:基础功能的BeanFactory如XmlBeanFactory以及提供了高级功能的ApplicationContext,如以前SSM环境下场景的XmlWebApplicationContext与ClasspathXmlApplicationContext。

我们再回顾一下其继承树体系结构,如下图所示。
[读书笔记]Spring中IOC容器中XmlBeanFactory的初始化详解_加载
可以看到其明显继承于两大体系:BeanFactory体系与AliasRegistry体系。如上图所示​​​XmlBeanFactory​​​其实是过期的,目前常常把​​DefaultListableBeanFactory​​作为默认的BeanFactory实现使用。

@Deprecated
@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {

// 实例化XmlBeanDefinitionReader
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


// 使用XML Resource 实例化XmlBeanFactory
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}

// 使用XML resource 和parentBeanFactory实例化XmlBeanFactory
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
}

从上述源码可以看到,其实例化过程主要有两个支线:​​super(parentBeanFactory);​​以及使用XmlBeanDefinitionReader 从 XML resource中加载Bean定义。

以前我们可能在测试的时候使用如下代码获取BeanFactory并注册bean:

//根据Xml配置文件创建Resource资源对象,该对象中包含了BeanDefinition的信息
ClassPathResource resource =new ClassPathResource("applicationContext.xml");

//创建DefaultListableBeanFactory
DefaultListableBeanFactory factory =new DefaultListableBeanFactory();

//创建XmlBeanDefinitionReader读取器,用于载入BeanDefinition。
//之所以需要BeanFactory作为参数,是因为会将读取的信息回调配置给factory
XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);

//XmlBeanDefinitionReader执行载入BeanDefinition的方法,最后会完成Bean的载入和注册。完成
//后Bean就成功的放置到IOC容器当中,以后我们就可以从中取得Bean来使用
reader.loadBeanDefinitions(resource);

① super支线

//XmlBeanFactory
super(parentBeanFactory);

// DefaultListableBeanFactory
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}

//AbstractAutowireCapableBeanFactory
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
this();
setParentBeanFactory(parentBeanFactory);
}
// AbstractAutowireCapableBeanFactory-- this();
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}

//AbstractBeanFactory
public AbstractBeanFactory() {
}
// //AbstractBeanFactory -- setParentBeanFactory
@Override
public void setParentBeanFactory(@Nullable BeanFactory parentBeanFactory) {
if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
}
this.parentBeanFactory = parentBeanFactory;
}

也就是沿着其父类体系走到了AbstractBeanFactory这个抽象基类中。


② reader.loadBeanDefinitions(resource)

首先实例化​​XmlBeanDefinitionReader​​​,然后调用其​​loadBeanDefinitions​​方法。

//loadBeanDefinitions 方法将Resource 包装为了EncodedResource 然后交给了下面方法
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}

// 这个才是真正核心入口方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}

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!");
}
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<Set<EncodedResource>> resourcesCurrentlyBeingLoaded​​​来维护当前线程加载的资源,使用HashSet避免重复加载。然后从resource读取到InputStream调用​​doLoadBeanDefinitions(inputSource, encodedResource.getResource());​​方法进行处理。

也就是说这里​​int loadBeanDefinitions(EncodedResource encodedResource)​​方法可以理解为是一个入口&逻辑控制方法,维护当前线程上下文的资源加载、控制资源重复加载。

​doLoadBeanDefinitions(InputSource inputSource, Resource resource)​​​方法其实是一个模板方法。核心方法是​​doLoadDocument​​​读取到文档对象,然后调用​​registerBeanDefinitions​​​方法注册bean并统计bean的个数。其余就是一些异常控制,根据异常类型抛出​​XmlBeanDefinitionStoreException​​​或者​​BeanDefinitionStoreException​​。将异常交给外层去处理。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {

try {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}

③ 获取Document对象

方法如下所示,根据一个​​documentLoader​​​去加载获取​​Document​​​对象。这里​​documentLoader​​​是​​DefaultDocumentLoader​​实例。

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}

按照Spring生态惯例来讲,其应该伴随着XXXBuilderFactory、XXXBuilder、XXXParser三大工具。有时会有XXXValidator、XXXErrorHandler校验与错误处理。这里我们简单跟一下其获取Document对象的过程。


在DefaultDocumentLoader中其首先获取文档对象建造者工厂,然后使用工厂获取建造者实例,使用建造者实例解析得到文档对象。PS:这里应用了工厂模式与建造者模式。

@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}

其中在获取建造者实例时,为其赋予了​​entityResolver​​​和​​errorHandler​​两个工具。前者用来解析实体,后者用来进行错误处理。源码如下所示:

protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException {

DocumentBuilder docBuilder = factory.newDocumentBuilder();
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}

④ 注册bean定义

核心入口方法:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

这里首先获取BeanDefinitionDocumentReader ,然后使用该read去注册bean定义。此外还获取读取前、读取后注册bean定义,将本次读取的bean定义数量作为返回结果。

​BeanDefinitionDocumentReader​​中注册bean定义入口方法如下所示:

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}

​doRegisterBeanDefinitions(Element root)​​ 也是个模板方法,如下所示其将会从给定的根元素解析每一个定义的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;
}

这里delegate 与parent用来进行嵌套bean定义注册。​​preProcessXml(root)​​​与​​postProcessXml(root)​​​是提供了两个扩展点,用于用户进行before/after—processing扩展。这两个方法默认是空实现,Reader核心方法通过​​parseBeanDefinitions(root, this.delegate);​​解析每一个element。

如下所示:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}

这里我们看一下​​parseDefaultElement​​​方法,其根据节点名字进行不同处理。如果节点node名字是​​beans​​​那就递归调用​​doRegisterBeanDefinitions(ele)​​​方法。如果节点名字是​​bean​​那么就会调用processBeanDefinition方法解析element并注册。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// 解析import标签
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// 解析alias
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// 解析并注册bean定义
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse 递归解析
doRegisterBeanDefinitions(ele);
}
}

关于常量成员如下所示:

// bean
public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;

public static final String NESTED_BEANS_ELEMENT = "beans";

public static final String ALIAS_ELEMENT = "alias";

public static final String NAME_ATTRIBUTE = "name";

public static final String ALIAS_ATTRIBUTE = "alias";

public static final String IMPORT_ELEMENT = "import";

public static final String RESOURCE_ATTRIBUTE = "resource";

public static final String PROFILE_ATTRIBUTE = "profile";

​processBeanDefinition​​​方法如下所示其会尝试获取​​BeanDefinitionHolder​​​,如果不为null,就尝试对其进行装饰然后注册bean到​​BeanDefinitionRegistry​​​中。如注册到DefaultListableBeanFactory的属性​​private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);​​中。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}