Spring 命名空间的解析

以 ClassPathXmlApplicationContext 方式启动容器为例

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

refresh方法中依次调用:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

然后下面列出类与方法,org.springframework 简写为 o.s   类名与方法名之间用#分隔 

o.s.context.support.AbstractApplicationContext#obtainFreshBeanFactory
o.s.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory
o.s.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(o.s.beans.factory.support.DefaultListableBeanFactory)
o.s.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(o.s.beans.factory.xml.XmlBeanDefinitionReader)
o.s.beans.factory.support.AbstractBeanDefinitionReader#loadBeanDefinitions(o.s.core.io.Resource...)
o.s.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(o.s.core.io.Resource)
o.s.beans.factory.xml.XmlBeanDefinitionReader#loadBeanDefinitions(o.s.core.io.support.EncodedResource)
o.s.beans.factory.xml.XmlBeanDefinitionReader#doLoadBeanDefinitions()
o.s.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions()

然后我们继续看registerBeanDefinitions方法的具体实现代码

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        //这里实际是 DefaultBeanDefinitionDocumentReader
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();
        //1
		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 等

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

 

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

参考资料:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值