[读书笔记]Spring中IOC容器中XmlBeanFactory的初始化详解

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

我们再回顾一下其继承树体系结构,如下图所示。
在这里插入图片描述
可以看到其明显继承于两大体系: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对象。这里documentLoaderDefaultDocumentLoader实例。

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);
}

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

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));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值