前言
前面我们说过ClassPathXmlApplicationContext类的refresh方法,是Spring IOC的入口方法。
这个类主要是处理Spring项目的classPath下Xml文件的,处理Annotation的类是AnnotationConfigApplicationContext,后面会给大家做详细介绍的。
这篇文章重点跟大家一起聊聊,Spring是如何读取xml文件的。
1 回顾xml文件的用法
上一节,我们聊到通过AbstractRefreshableApplicationContext类的refreshBeanFactory方法中的createBeanFactory方法,可以创建一个Spring容器,即:beanFactory对象。
然后将beanFactory对象作为参数,传入了loadBeanDefinitions方法中,该方法是今天主角:
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}这个方法这样的:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建一个XmlBeanDefinitionReader对象实例
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 设置资源加载的环境,比如:dev、st或者ga环境
beanDefinitionReader.setEnvironment(this.getEnvironment());
// 设置资源加载器
beanDefinitionReader.setResourceLoader(this);
// 设置实体解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//初始化beanDefinitionReader对象,设置文件是否需要验证
initBeanDefinitionReader(beanDefinitionReader);
//加载BeanDefinition
loadBeanDefinitions(beanDefinitionReader);
}首先会创建一个XmlBeanDefinitionReader类的实例,用它来读取Xml文件。
接下来,给该对象设置一些参数。
然后做一些初始化的工作。
最后会调用loadBeanDefinitions方法:
2 loadBeanDefinitions方法
接下来,重点看loadBeanDefinitions方法:
该方法会循环locations(applicationContext.xml文件路径),调用另外一个loadBeanDefinitions方法,一个文件一个文件解析。
经过一些列的骚操作,会将location转换成inputSource和resource,然后再转换成Document对象,方便解析。
3 解析xml文件
接下来,重点看看Spring是如何解析applicationContext.xml文件的:
其中的registerBeanDefinitions方法,里面的逻辑是这样的:
重点是调用documentReader对象的registerBeanDefinitions方法,后面会调用到DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法:
这个方法会创建一个BeanDefinitionParserDelegate对象,去出来xml解析逻辑,使用了一种委托模式。
preProcessXml和postProcessXml是两个空方法,给子类扩展用的。
其中的parseBeanDefinitions方法时解析xml的真正入口:
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);
}
}在解析xml文件时,需要判断是默认标签,还是自定义标签,处理逻辑不一样:
spring的默认标签只有4种:
- <import/>
- <alias/>
- <bean/>
- <beans/>
对应的处理方法是:
注意常见的:<aop/>、<context/>、<mvc/>等都是自定义标签。
从上图中处理<bean/>标签的processBeanDefinition方法开始,经过一系列调用,最终会调到DefaultBeanDefinitionDocumentReader类的processBeanDefinition方法。
这个方法包含了关键步骤:解析元素生成BeanDefinition 和 注册BeanDefinition。
4 解析自定义标签
用户自定义的标签,是通过BeanDefinitionParserDelegate类的parseCustomElement方法解析的:
最后是通过NamespaceHandlerSupport类的parse方法解析的自定义标签:
这个解析中使用BeanDefinitionParser类去真正的解析标签,目前Sping已经定义了很多BeanDefinitionParser的实现类:
在AbstractBeanDefinitionParser类的parse方法中解析自定义标签:
这个方法底层逻辑很深,感兴趣的小伙伴,可以找我私聊。
我们下期再见。。。