Spring 命令空间的解析

以 ClassPathXmlApplicationContext 方式启动容器为例

首先是ClassPathXmlApplicationContext的构造器中的refresh方法

接着依次调用的方法refresh方法中的 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

org.springframework.context.support.AbstractApplicationContext#obtainFreshBeanFactory

org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)

org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.xml.XmlBeanDefinitionReader)

 

org.springframework.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource...)

 

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.Resource)

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions

org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //这里实际是 DefaultBeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}

继续进入 documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

可以看到调用了doRegisterBeanDefinitions(root);  代码如下

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);
//如果是默认命令空间处理,但我们本次要看的是非默认命令空间 如 context 因此暂时不看。
		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);
				if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
					return;
				}
			}
		}

		preProcessXml(root);
//这里是重点 是对元素的解析
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

		this.delegate = parent;
	}
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 {//非默认命令空间, 如 context等的解析处理 继续追踪这个方法代码
			delegate.parseCustomElement(root);
		}
	}

接下来 就比较简单了,代码也比较简单不再详述。简要的说:

1、根据传入的element 找到对应的命令空间字符串

2、扫描classpath下所有的META-INF/spring.handlers文件,形成map

3、使用第1步中的字符串,匹配到NamespaceHandler的具体实现类并实例化

4、调用init方法,注入对应localname 的处理器,parse方法,找到具体的localname的解析器

5、调用对应解析器的parse方法

比如 context:component-scan 对应的处理,我们打开spring-context jar文件,找到spring.handlers文件,就会看到它context命令空间处理器对应为 org.springframework.context.config.ContextNamespaceHandler

查看其中的init方法,就知道 component-scan 对应的解析器为

 org.springframework.context.annotation.ComponentScanBeanDefinitionParser

另:

通过查看ComponentScanBeanDefinitionParser 的源码,我们看到component-scan 默认具有annotation-config的功能(看官方文档也能知道)。它的作用就是:

1、扫描指定路径下被component注解标注的类(当然也能扫描到Service Repository Controller 因为这些是它的子类),作为bean,注入到容器中。

2、注入了一些注册配置处理器,以向被autowired resource等注解标记的属性注入bean(即依赖注入),有如下一些出来器

ConfigurationClassPostProcessor  AutowiredAnnotationBeanPostProcessor  RequiredAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor 等

(具体请看方法源码:org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object))

 

最后,在ComponentScanBeanDefinitionParser的parse 方法中打一个断点,以看到整个全貌,便于整体理解。

Spring 命令空间的解析

参考资料:

  https://javadoop.com/post/spring-ioc