前言

前面我们说过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方法:image该方法会循环locations(applicationContext.xml文件路径),调用另外一个loadBeanDefinitions方法,一个文件一个文件解析。

经过一些列的骚操作,会将location转换成inputSource和resource,然后再转换成Document对象,方便解析。

3 解析xml文件

接下来,重点看看Spring是如何解析applicationContext.xml文件的:image其中的registerBeanDefinitions方法,里面的逻辑是这样的:image重点是调用documentReader对象的registerBeanDefinitions方法,后面会调用到DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法:image这个方法会创建一个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文件时,需要判断是默认标签,还是自定义标签,处理逻辑不一样:image

spring的默认标签只有4种:

  1. <import/>
  2. <alias/>
  3. <bean/>
  4. <beans/>

image对应的处理方法是:image注意常见的:<aop/>、<context/>、<mvc/>等都是自定义标签。

从上图中处理<bean/>标签的processBeanDefinition方法开始,经过一系列调用,最终会调到DefaultBeanDefinitionDocumentReader类的processBeanDefinition方法。image

这个方法包含了关键步骤:解析元素生成BeanDefinition 和 注册BeanDefinition。

4 解析自定义标签

用户自定义的标签,是通过BeanDefinitionParserDelegate类的parseCustomElement方法解析的:image最后是通过NamespaceHandlerSupport类的parse方法解析的自定义标签:image这个解析中使用BeanDefinitionParser类去真正的解析标签,目前Sping已经定义了很多BeanDefinitionParser的实现类:

image在AbstractBeanDefinitionParser类的parse方法中解析自定义标签:image这个方法底层逻辑很深,感兴趣的小伙伴,可以找我私聊。

我们下期再见。。。

最后修改:2026 年 06 月 05 日
如果觉得我的文章对你有用,请随意赞赏