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 方法中打一个断点,以看到整个全貌,便于整体理解。
参考资料: