IoC容器的初始化包括BeanDefinition
的Resource定位、载入和注册这三个基本的过程。从[读书笔记]Spring中的容器设计一文可知IOC容器主要有两个体现:基础功能的BeanFactory如XmlBeanFactory以及提供了高级功能的ApplicationContext,如以前SSM环境下场景的XmlWebApplicationContext与ClasspathXmlApplicationContext。
我们再回顾一下其继承树体系结构,如下图所示。
可以看到其明显继承于两大体系:BeanFactory体系与AliasRegistry体系。如上图所示XmlBeanFactory
其实是过期的,目前常常把DefaultListableBeanFactory
作为默认的BeanFactory实现使用。
({"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( BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}
//AbstractAutowireCapableBeanFactory
public AbstractAutowireCapableBeanFactory( BeanFactory parentBeanFactory) {
this();
setParentBeanFactory(parentBeanFactory);
}
// AbstractAutowireCapableBeanFactory-- this();
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
//AbstractBeanFactory
public AbstractBeanFactory() {
}
// //AbstractBeanFactory -- setParentBeanFactory
public void setParentBeanFactory( 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 然后交给了下面方法
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:这里应用了工厂模式与建造者模式。
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,
EntityResolver entityResolver, 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定义入口方法如下所示:
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
doRegisterBeanDefinitions(Element root)
也是个模板方法,如下所示其将会从给定的根元素解析每一个定义的bean:
"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));
}
}