news 2026/4/17 21:20:33

Spring XML解析与BeanDefinition注册详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring XML解析与BeanDefinition注册详解

一、XML解析为BeanDefinition的时机

1.1 在Spring生命周期中的位置

XML解析为BeanDefinition发生在Spring容器启动阶段,具体时机如下:

Spring容器启动流程: 1. 创建BeanFactory 2. 【XML解析阶段】加载配置文件,解析XML,注册BeanDefinition ← 发生在这里 3. BeanFactoryPostProcessor处理 4. 实例化Bean 5. 依赖注入 6. 初始化Bean 7. 容器就绪

1.2 详细执行流程

ClassPathXmlApplicationContext为例:

// 1. 构造器调用publicClassPathXmlApplicationContext(StringconfigLocation){this(newString[]{configLocation},true,null);}// 2. refresh()方法 - Spring容器启动的核心方法publicvoidrefresh()throwsBeansException{synchronized(this.startupShutdownMonitor){// 准备刷新上下文prepareRefresh();// 【关键步骤】获取BeanFactory,在此过程中完成XML解析ConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();// 后续步骤...prepareBeanFactory(beanFactory);postProcessBeanFactory(beanFactory);invokeBeanFactoryPostProcessors(beanFactory);registerBeanPostProcessors(beanFactory);// ...}}// 3. obtainFreshBeanFactory() - 触发XML解析protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){refreshBeanFactory();// 在这里进行XML解析returngetBeanFactory();}// 4. refreshBeanFactory() - AbstractRefreshableApplicationContextprotectedfinalvoidrefreshBeanFactory(){// 创建BeanFactoryDefaultListableBeanFactorybeanFactory=createBeanFactory();// 【核心】加载BeanDefinitionloadBeanDefinitions(beanFactory);}// 5. loadBeanDefinitions() - AbstractXmlApplicationContextprotectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory){// 创建XML的BeanDefinition读取器XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory);// 使用Reader读取XML并注册BeanDefinitionloadBeanDefinitions(beanDefinitionReader);}

1.3 时机总结

阶段说明
发生时机容器refresh()方法的obtainFreshBeanFactory()阶段
在Bean实例化之前此时只是注册元数据,Bean还未创建
在BeanFactoryPostProcessor之前BeanFactoryPostProcessor可以修改BeanDefinition
可修改性此阶段注册的BeanDefinition可被后续处理器修改

二、XML解析方式详解

Spring中主要有两种XML解析方式DOM解析SAX解析。Spring默认使用DOM解析。

2.1 DOM(Document Object Model)解析

2.1.1 基本原理
工作流程: XML文件 → 完整读入内存 → 构建DOM树 → 遍历节点 → 提取信息
2.1.2 Spring中的使用

Spring通过DocumentLoader加载XML为DOM Document:

// DefaultDocumentLoader.javapublicDocumentloadDocument(InputSourceinputSource,EntityResolverentityResolver,ErrorHandlererrorHandler,intvalidationMode,booleannamespaceAware){// 创建DocumentBuilderFactoryDocumentBuilderFactoryfactory=createDocumentBuilderFactory(validationMode,namespaceAware);// 创建DocumentBuilderDocumentBuilderbuilder=createDocumentBuilder(factory,entityResolver,errorHandler);// 解析为Document对象(DOM树)returnbuilder.parse(inputSource);}
2.1.3 解析过程
// 1. 加载DocumentDocumentdoc=documentLoader.loadDocument(...);// 2. 获取根元素Elementroot=doc.getDocumentElement();// 3. 遍历解析(以bean标签为例)NodeListnodeList=root.getElementsByTagName("bean");for(inti=0;i<nodeList.getLength();i++){ElementbeanElement=(Element)nodeList.item(i);// 4. 提取属性Stringid=beanElement.getAttribute("id");StringclassName=beanElement.getAttribute("class");Stringscope=beanElement.getAttribute("scope");// 5. 创建BeanDefinitionBeanDefinitionbd=newGenericBeanDefinition();bd.setBeanClassName(className);bd.setScope(scope);// 6. 解析子元素(property、constructor-arg等)parsePropertyElements(beanElement,bd);// 7. 注册到BeanFactoryregistry.registerBeanDefinition(id,bd);}
2.1.4 优势
优势说明
随机访问可以随意访问DOM树的任意节点
双向遍历可以从父节点到子节点,也可以从子节点到父节点
易于操作API简单直观,便于增删改查
支持修改可以修改DOM树的结构和内容
支持XPath可以使用XPath快速定位节点
2.1.5 劣势
劣势说明
内存占用大需要将整个XML加载到内存构建DOM树
解析速度慢大文件解析时需要较长时间
不适合大文件大型XML文件可能导致内存溢出
解析前必须完整必须等待整个文档加载完成
2.1.6 适用场景
  • ✅ Spring配置文件(通常较小,需要随机访问)
  • ✅ 需要频繁访问不同节点
  • ✅ 需要修改XML内容
  • ❌ 超大型XML文件(几百MB以上)

2.2 SAX(Simple API for XML)解析

2.2.1 基本原理
工作流程: XML文件 → 边读边解析 → 触发事件 → 事件处理器处理 → 不保留完整树结构

SAX采用事件驱动模型

// SAX事件处理器publicclassMyHandlerextendsDefaultHandler{// 1. 文档开始事件publicvoidstartDocument(){System.out.println("开始解析文档");}// 2. 元素开始事件publicvoidstartElement(Stringuri,StringlocalName,StringqName,Attributesattributes){if("bean".equals(qName)){Stringid=attributes.getValue("id");StringclassName=attributes.getValue("class");// 处理bean定义...}}// 3. 元素内容事件publicvoidcharacters(char[]ch,intstart,intlength){Stringcontent=newString(ch,start,length);// 处理文本内容...}// 4. 元素结束事件publicvoidendElement(Stringuri,StringlocalName,StringqName){if("bean".equals(qName)){// bean标签结束,注册BeanDefinition}}// 5. 文档结束事件publicvoidendDocument(){System.out.println("文档解析完成");}}
2.2.2 解析过程
// 1. 创建SAX解析器工厂SAXParserFactoryfactory=SAXParserFactory.newInstance();// 2. 创建解析器SAXParserparser=factory.newSAXParser();// 3. 创建事件处理器MyHandlerhandler=newMyHandler();// 4. 开始解析(触发事件)parser.parse(newFile("beans.xml"),handler);
2.2.3 事件触发顺序示例

对于以下XML:

<?xml version="1.0" encoding="UTF-8"?><beans><beanid="userService"class="com.example.UserService"><propertyname="name"value="test"/></bean></beans>

SAX解析触发的事件序列:

1. startDocument() 2. startElement("beans") 3. startElement("bean", {id="userService", class="com.example.UserService"}) 4. startElement("property", {name="name", value="test"}) 5. endElement("property") 6. endElement("bean") 7. endElement("beans") 8. endDocument()
2.2.4 优势
优势说明
内存占用小不需要加载整个文档到内存
解析速度快边读边解析,无需等待完整加载
适合大文件可以处理超大型XML文件
流式处理支持流式数据处理
2.2.5 劣势
劣势说明
单向解析只能从前往后解析,不能回退
无法随机访问不能直接访问特定节点
编程复杂需要手动维护状态,代码较复杂
不能修改无法修改XML内容
需要手动管理状态需要在事件处理器中维护上下文信息
2.2.6 适用场景
  • ✅ 超大型XML文件解析
  • ✅ 流式数据处理
  • ✅ 内存受限环境
  • ❌ 需要随机访问节点
  • ❌ 需要修改XML

2.3 其他解析方式

2.3.1 StAX(Streaming API for XML)

StAX是拉式解析(Pull Parsing),介于DOM和SAX之间:

XMLInputFactoryfactory=XMLInputFactory.newInstance();XMLStreamReaderreader=factory.createXMLStreamReader(newFileInputStream("beans.xml"));while(reader.hasNext()){intevent=reader.next();// 主动拉取事件if(event==XMLStreamConstants.START_ELEMENT){StringtagName=reader.getLocalName();if("bean".equals(tagName)){Stringid=reader.getAttributeValue(null,"id");StringclassName=reader.getAttributeValue(null,"class");// 处理...}}}

特点对比

特性SAXStAX
模型推式(事件驱动)拉式(迭代器模式)
控制解析器控制流程应用程序控制流程
易用性需要回调处理更直观,类似迭代器
性能略快略慢
2.3.2 JDOM 和 DOM4J

这些是第三方XML解析库,不是Spring默认使用的方式:

// DOM4J示例SAXReaderreader=newSAXReader();Documentdocument=reader.read(newFile("beans.xml"));Elementroot=document.getRootElement();for(Elementbean:root.elements("bean")){Stringid=bean.attributeValue("id");StringclassName=bean.attributeValue("class");// 处理...}

三、Spring为什么选择DOM解析

3.1 Spring的选择理由

理由说明
配置文件通常较小Spring配置文件一般不超过几MB,DOM的内存开销可接受
需要随机访问解析bean依赖关系需要随机访问不同节点
需要支持命名空间Spring支持自定义命名空间,DOM更容易处理
易于扩展DOM API更容易实现自定义标签解析
向后兼容历史原因,保持API稳定性

3.2 Spring的优化措施

虽然使用DOM,但Spring做了很多优化:

// 1. 延迟加载BeanDefinition// 只在第一次获取Bean时才解析// 2. 缓存解析结果// BeanDefinition注册后会缓存,避免重复解析// 3. 支持@Lazy注解// 延迟Bean的实例化// 4. 支持条件注册// @Conditional可以跳过不必要的Bean注册

四、DOM vs SAX 深度对比

4.1 技术对比表

对比维度DOMSAX
解析方式树形结构,全部加载事件驱动,边读边解析
内存占用高(与文件大小成正比)低(固定大小)
解析速度慢(需要构建完整树)快(流式处理)
访问方式随机访问顺序访问
遍历方向双向单向(前向)
修改能力支持不支持
API复杂度简单直观复杂(需要状态管理)
适用场景中小型文件、需要修改大型文件、流式处理
XPath支持支持不支持
线程安全不安全(需要同步)每个解析器独立使用

4.2 性能对比

以10MB的XML文件为例:

DOM解析: - 内存占用:约40-50MB(4-5倍文件大小) - 解析时间:约2-3秒 - 优势:解析后访问快速 SAX解析: - 内存占用:约5-10MB(固定开销) - 解析时间:约0.5-1秒 - 优势:内存友好,适合大文件

4.3 代码复杂度对比

DOM解析代码(简洁)

Documentdoc=builder.parse(xmlFile);NodeListbeans=doc.getElementsByTagName("bean");for(inti=0;i<beans.getLength();i++){Elementbean=(Element)beans.item(i);Stringid=bean.getAttribute("id");// 直接访问,逻辑清晰}

SAX解析代码(复杂)

publicclassBeanHandlerextendsDefaultHandler{privateStack<String>elementStack=newStack<>();privateBeanDefinitioncurrentBean;publicvoidstartElement(Stringuri,StringlocalName,StringqName,Attributesattributes){elementStack.push(qName);if("bean".equals(qName)){currentBean=newBeanDefinition();currentBean.setId(attributes.getValue("id"));}elseif("property".equals(qName)&&isInBean()){// 需要手动维护状态}}privatebooleanisInBean(){returnelementStack.contains("bean");}// 需要更多状态管理代码...}

五、Spring XML解析核心类

5.1 核心类图

XmlBeanDefinitionReader(XML读取器) ├── DocumentLoader(文档加载器) │ └── DefaultDocumentLoader(默认实现,使用DOM) │ ├── BeanDefinitionDocumentReader(文档解析器) │ └── DefaultBeanDefinitionDocumentReader │ ├── parseBeanDefinitions() - 解析beans标签 │ └── processBeanDefinition() - 解析bean标签 │ └── BeanDefinitionParserDelegate(解析委托) ├── parseBeanDefinitionElement() - 解析bean元素 ├── parsePropertyElements() - 解析property元素 └── parseConstructorArgElements() - 解析constructor-arg元素

5.2 关键方法调用链

// 1. XmlBeanDefinitionReader.loadBeanDefinitions()publicintloadBeanDefinitions(Resourceresource){returnloadBeanDefinitions(newEncodedResource(resource));}// 2. doLoadBeanDefinitions()protectedintdoLoadBeanDefinitions(InputSourceinputSource,Resourceresource){// 加载Document(使用DOM)Documentdoc=doLoadDocument(inputSource,resource);// 注册BeanDefinitionreturnregisterBeanDefinitions(doc,resource);}// 3. registerBeanDefinitions()publicintregisterBeanDefinitions(Documentdoc,Resourceresource){BeanDefinitionDocumentReaderdocumentReader=createBeanDefinitionDocumentReader();// 委托给DocumentReader处理documentReader.registerBeanDefinitions(doc,createReaderContext(resource));}// 4. DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()protectedvoiddoRegisterBeanDefinitions(Elementroot){BeanDefinitionParserDelegateparent=this.delegate;this.delegate=createDelegate(getReaderContext(),root,parent);// 解析beans标签parseBeanDefinitions(root,this.delegate);}// 5. parseBeanDefinitions() - 遍历子元素protectedvoidparseBeanDefinitions(Elementroot,BeanDefinitionParserDelegatedelegate){NodeListnl=root.getChildNodes();for(inti=0;i<nl.getLength();i++){Nodenode=nl.item(i);if(nodeinstanceofElement){Elementele=(Element)node;if(delegate.isDefaultNamespace(ele)){// 默认命名空间(bean、import、alias等)parseDefaultElement(ele,delegate);}else{// 自定义命名空间(aop、tx、context等)delegate.parseCustomElement(ele);}}}}

六、实际应用建议

6.1 Spring配置文件优化

<!-- ❌ 避免:单个超大配置文件 --><beans><!-- 上千个bean定义... --></beans><!-- ✅ 推荐:拆分为多个模块 --><beans><importresource="spring-dao.xml"/><importresource="spring-service.xml"/><importresource="spring-web.xml"/></beans>

6.2 何时考虑SAX

如果需要处理超大型XML(非Spring配置场景):

// 场景:处理1GB的数据导出XMLpublicclassLargeXmlProcessorextendsDefaultHandler{privateintrecordCount=0;@OverridepublicvoidstartElement(Stringuri,StringlocalName,StringqName,Attributesattributes){if("record".equals(qName)){// 处理单条记录processRecord(attributes);recordCount++;if(recordCount%10000==0){System.out.println("Processed "+recordCount+" records");}}}privatevoidprocessRecord(Attributesattributes){// 处理并立即释放,不保留在内存}}

6.3 现代Spring项目建议

// 优先使用注解配置,避免XML@Configuration@ComponentScan("com.example")publicclassAppConfig{@BeanpublicUserServiceuserService(){returnnewUserServiceImpl();}}// 或使用Spring Boot@SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String[]args){SpringApplication.run(Application.class,args);}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 21:11:15

2025年实测!6款降AI率和查ai率工具汇总!

在论文、报告、内容创作越来越严格的时代&#xff0c;查AI率、检测AI率、降AI率 已经成为学生、写作者、博主的日常需求。很多同学因为 AI率过高被导师指出“AI痕迹太重”&#xff0c;甚至退回重写。本文今天一次性告诉你&#xff1a; 检测AI率应该注意什么 免费查AI率的网站有…

作者头像 李华
网站建设 2026/4/18 5:43:13

ContextMenuManager:Windows右键菜单终极优化指南

ContextMenuManager&#xff1a;Windows右键菜单终极优化指南 【免费下载链接】ContextMenuManager &#x1f5b1;️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 当您的Windows右键菜单因安装过多软件而变得臃肿不…

作者头像 李华
网站建设 2026/4/12 22:04:07

2025年12月毕业生最爱的6款降AI神器推荐(含免费查AI率工具)

在论文、报告、内容创作越来越严格的时代&#xff0c;查AI率、检测AI率、降AI率 已经成为学生、写作者、博主的日常需求。很多同学因为 AI率过高被导师指出“AI痕迹太重”&#xff0c;甚至退回重写。本文今天一次性告诉你&#xff1a; 检测AI率应该注意什么 免费查AI率的网站有…

作者头像 李华
网站建设 2026/4/18 5:40:05

PyTorch + OpenMMLab 等专用计算机视觉框架介绍

本篇博文详细介绍计算机视觉与深度学习整合使用的开源框架&#xff0c;涵盖主流框架、专用工具库以及发展趋势&#xff1a; 一、主流综合深度学习框架&#xff08;内置CV支持&#xff09; 1. PyTorch&#xff08;目前研究领域主流&#xff09; 特点&#xff1a;动态计算图、Pyt…

作者头像 李华
网站建设 2026/4/18 8:00:04

12、社会工程学攻击工具:SET与BeEF深度解析

社会工程学攻击工具:SET与BeEF深度解析 1. 社会工程学攻击概述 社会工程学攻击利用人们的信任来突破安全防线。大多数安全架构旨在阻止未经邀请的访客进入目标网络,但攻击者可以欺骗内部用户开门放行。攻击者常伪装成权威人士或家庭成员来获取受害者的信任,成功后可能获得…

作者头像 李华
网站建设 2026/4/18 6:40:07

应用运维目录

应用运维目录一、写在前面二、应用分类ApacheMySQLJavaPythonDockerNginxMinIONacosZabbix一、写在前面 为了方便后面查找和更新&#xff0c;在这里列出跟应用有关的文档连接。 二、应用分类 Apache 1、安装教程 2、报错处理 MySQL 1、安装教程 CentOS7下安装MySql 2、…

作者头像 李华