Apache XML Commons External 介绍与选型指南

梳理 Apache XML Commons External(xml-apis)标准 XML 接口 jar 的定位、版本历史与 jar 内容实测结论,辨析其与 JDK 内置接口、Xerces-J 的配套关系并给出选型建议。

Apache XML Commons ExternalApache XML Commons ExternalJavaXMLApache2026/10/16

Apache XML Commons External 介绍与选型指南

目标:讲清 Apache XML Commons External(对外坐标 xml-apis:xml-apis)这个"标准 XML 接口 jar"的定位、版本历史与 jar 内容构成,实测辨析它与 JDK 内置 XML 接口、Apache Xerces-J 的配套关系,给出"现代项目还需不需要引入"的选型结论。全部命令与回显已在 Ubuntu 22.04 + OpenJDK 17(另装 OpenJDK 8 做历史对照)+ Maven 3.9.9 实测通过。

一、环境信息

本章列出实测环境与版本核验命令,后续所有命令与回显均来自该环境。

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64),主测 JDK:OpenJDK 17.0.20.1,对照 JDK:OpenJDK 1.8.0_504
  • 构建工具:Apache Maven 3.9.9(解压于 /root/apache-maven-3.9.9)
  • 实测对象:xml-apis 1.4.01、xml-apis 2.0.2、xml-apis-ext 1.3.04、xercesImpl 2.12.2
  • 实验目录:/root/xmltest(jar 下载盘点)、/root/factorydemo(工厂加载演示)、/root/endorsed(JDK8 endorsed 实验)

版本核验(实测回显):

java -version          # 主测 JDK
/usr/lib/jvm/java-8-openjdk-amd64/bin/java -version   # 对照 JDK
openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
openjdk version "1.8.0_504"
OpenJDK Runtime Environment (build 1.8.0_504-8u504-ga-1ubuntu1~22.04.3-b01)
OpenJDK 64-Bit Server VM (build 25.504-b01, mixed mode)

二、软件概述

2.1 项目定位

Apache XML Commons 是 xml.apache.org 下的基础子项目,自身不实现任何 XML 解析,职责是把 W3C DOM、SAX、JAXP、StAX 这些由 W3C、SAX 项目(David Megginson)、Sun/BEA 制定的标准接口编译成一份 Apache 托管的接口 jar,供 Xerces-J、Xalan-J 引用同一份契约,并向 Apache 体系之外(Batik、Fop、老版 Axis 等第三方项目)发布——这就是 "External" 的含义。对外坐标为 xml-apis:xml-apis(主接口包)与 xml-apis:xml-apis-ext(DOM 扩展包)。

💡 技术拓展:一套 XML 标准接口存在多种处理 API,各有分工——

API处理模型特点典型场景
SAX流式、事件回调顺序读取、内存占用极低、不可回溯大文件顺序解析
DOM树模型、整棵加载可随机访问、可写回、内存开销大需改写或随机遍历文档
StAX流式、拉取式调用方主动 next() 拉事件,比 SAX 直观中等规模流式处理
JAXP工厂门面统一入口发现解析器实现,屏蔽底层实现几乎所有 Java XML 程序

xml-apis 1.4.01 把四类接口打进同一个 jar(三章实测),本文以 JAXP 工厂机制为主线展开。

2.2 Maven Central 版本全表(实测)

确认坐标 xml-apis:xml-apis 到底有哪些版本(输出节选,实测首尾):

curl -sS https://repo1.maven.org/maven2/xml-apis/xml-apis/maven-metadata.xml
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
  <groupId>xml-apis</groupId>
  <artifactId>xml-apis</artifactId>
  <versioning>
    <latest>2.0.2</latest>
    <release>2.0.2</release>
    <versions>
      <version>1.0.b2</version>
      <version>1.2.01</version>
      <version>1.3.02</version>
      <version>1.3.03</version>
      <version>1.3.04</version>
      <version>1.4.01</version>
      <version>2.0.0</version>
      <version>2.0.2</version>
    </versions>
    <lastUpdated>20120626170101</lastUpdated>
  </versioning>
</metadata>

两个事实值得注意:最新与 release 版本是 2.0.2 而非流传较广的 1.4.01;元数据 lastUpdated 为 2012-06-26,此后项目在 Central 上再未更新。坊间常提的 "1.4.02" 并不存在,实测请求该版本直接 404:

curl -s -o /dev/null -w "1.4.02 http_code=%{http_code}\n" https://repo1.maven.org/maven2/xml-apis/xml-apis/1.4.02/xml-apis-1.4.02.jar
curl -s -o /dev/null -w "1.4.01 http_code=%{http_code}\n" https://repo1.maven.org/maven2/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar
1.4.02 http_code=404
1.4.01 http_code=200

用 jar 文件的 Last-Modified 响应头还原发布时间线:

curl -sI https://repo1.maven.org/maven2/xml-apis/xml-apis/2.0.2/xml-apis-2.0.2.jar | grep -i '^last-modified'
curl -sI https://repo1.maven.org/maven2/xml-apis/xml-apis/1.3.04/xml-apis-1.3.04.jar | grep -i '^last-modified'
curl -sI https://repo1.maven.org/maven2/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar | grep -i '^last-modified'
last-modified: Tue, 22 Nov 2005 18:28:38 GMT
last-modified: Fri, 22 Dec 2006 13:37:52 GMT
last-modified: Sat, 20 Aug 2011 00:11:11 GMT

即 2.0.0/2.0.2 于 2005-11-22 上传(面向 JAXP 1.4 时代、与 JDK 5+ 内置接口对齐的精简版)、1.3.04 于 2006-12-22(DOM 2 / JAXP 1.3 时代的主流版本,仍被大量老依赖传递引用)、1.4.01 于 2011-08-20(接口最全、生态引用最广)。版本号大小与发布时间并不单调,选型应以 jar 内容与目标 JDK 为准而非数字大小。

三、核心架构

3.1 jar 包内容盘点(实测)

下载四个实测对象并核对指纹(xercesImpl 为配套实现,一并取回用于对比):

mkdir -p /root/xmltest && cd /root/xmltest
B=https://repo1.maven.org/maven2
curl -sSLo xml-apis-1.4.01.jar $B/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar
curl -sSLo xml-apis-2.0.2.jar $B/xml-apis/xml-apis/2.0.2/xml-apis-2.0.2.jar
curl -sSLo xml-apis-ext-1.3.04.jar $B/xml-apis/xml-apis-ext/1.3.04/xml-apis-ext-1.3.04.jar
curl -sSLo xercesImpl-2.12.2.jar $B/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar
sha256sum xml-apis-1.4.01.jar xml-apis-2.0.2.jar
a840968176645684bb01aed376e067ab39614885f9eee44abe35a5f20ebe7fad  xml-apis-1.4.01.jar
8232f3482c346d843e5e3fb361055771c1acc105b6d8a189eb9018c55948cf9f  xml-apis-2.0.2.jar

四个 jar 实测大小:xml-apis-1.4.01 为 216K(346 个类)、xml-apis-2.0.2 为 107K(184 个类)、xml-apis-ext-1.3.04 为 84K(192 个类)、xercesImpl-2.12.2 为 1413K(962 个类)。用 jar tf 统计 1.4.01 的三级包结构:

jar tf xml-apis-1.4.01.jar | grep -c '\.class$'
jar tf xml-apis-1.4.01.jar | grep '\.class$' | awk -F/ '{print $1"/"$2"/"$3}' | sort | uniq -c | sort -rn
346
    150 org/w3c/dom
     44 org/xml/sax
     41 javax/xml/stream
     33 javax/xml/transform
     24 javax/xml/xpath
     19 javax/xml/validation
     15 javax/xml/parsers
     15 javax/xml/datatype
      3 javax/xml/namespace
      1 org/apache/xmlcommons
      1 javax/xml/XMLConstants

同口径看 xml-apis-ext 1.3.04 的构成(与主包完全不重叠):

jar tf xml-apis-ext-1.3.04.jar | grep '\.class$' | awk -F/ '{print $1"/"$2"/"$3"/"$4}' | sort | uniq -c | sort -rn
    161 org/w3c/dom/svg
     29 org/w3c/css/sac
      2 org/w3c/dom/smil

盘点结论:1.4.01 覆盖 org.w3c.dom、org.xml.sax 与 javax.xml 全家(transform/xpath/validation/parsers/datatype/namespace/stream),外加唯一的实现工具类 org.apache.xmlcommons.Version;xml-apis-ext 则是另一组内容——SVG DOM 接口(161 类)、SAC(CSS 解析接口,29 类)与 SMIL(2 类),供 Batik 等图形项目使用。

3.2 内置 Xerces 默认实现的设计(实测)

一个容易被忽略的细节:xml-apis 1.4.01 的工厂类内部把 Apache Xerces 的 JAXP 实现类硬编码为默认 fallback;MANIFEST 中也按包声明了承载的规格(SAX 2.0.2 (sax2r3)、DOM Level 3 Core/Load and Save、JAXP 1.4、StAX 1.0),可通过 unzip -p xml-apis-1.4.01.jar META-INF/MANIFEST.MF 复核。必须反编译 jar 内的类文件本身——用 javap 指定类名时 JVM 会优先解析到 JDK 内置同名类,得到 JDK 版本字节码,结论会失真;对解压出的类文件直接反编译:

mkdir -p /tmp/xjar && cd /tmp/xjar && unzip -oq /root/xmltest/xml-apis-1.4.01.jar 'javax/*'
javap -c -p /tmp/xjar/javax/xml/parsers/DocumentBuilderFactory.class | sed -n '/newInstance()/,/areturn/p' | head -6
javap -c -p /tmp/xjar/javax/xml/datatype/DatatypeFactory.class | grep '// String org' | head -1
  public static javax.xml.parsers.DocumentBuilderFactory newInstance();
    Code:
       0: ldc           #8                  // String javax.xml.parsers.DocumentBuilderFactory
       2: ldc           #9                  // String org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
       4: invokestatic  #10                 // Method javax/xml/parsers/FactoryFinder.find:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
       7: checkcast     #11                 // class javax/xml/parsers/DocumentBuilderFactory
      10: areturn
       4: ldc           #58                 // String org.apache.xerces.jaxp.datatype.DatatypeFactoryImpl

结论:xml-apis 1.4.01 把 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl(Xerces-J 的 JAXP 适配层)写死为默认实现类名(SAXParserFactory 同理为 org.apache.xerces.jaxp.SAXParserFactoryImpl)。它从设计上就是与 Xerces-J 成对交付:接口在 xml-apis、实现在 xercesImpl,缺一即抛错(JDK8 实测证据见 5.1)。

四、功能特性

4.1 接口覆盖面(实测汇总)

基于三章 jar tf 实测与 MANIFEST 规格声明,1.4.01 的覆盖面汇总:

包规格类数(实测)作用
org.w3c.dom(含 events/ls/ranges/traversal/views/bootstrap 子包)DOM Level 3150DOM 树模型接口
org.xml.sax(含 ext/helpers 子包)SAX 2.0.2 (sax2r3)44流式解析接口
javax.xml.streamStAX 1.041拉取式流处理接口
javax.xml.transform / xpath / validation / parsers / datatype / namespace / XMLConstantsJAXP 1.4111转换/XPath/校验/解析等 JAXP 门面
org.apache.xmlcommons.Version无1版本打印工具类

解读:xml-apis 是"四合一"接口包,比 2.0.2(184 类,仅 DOM/SAX/transform/parsers)覆盖广得多,这是 1.4.01 成为生态引用最广版本的原因;jar 里唯一的"活代码"可独立运行验证:

java -cp xml-apis-1.4.01.jar org.apache.xmlcommons.Version
XmlCommonsExternal 1.4.01

4.2 只有接口、没有实现、没有服务注册(实测)

xml-apis 不含任何解析器实现类,也不带 JAXP 服务注册文件,实测验证:

jar tf xml-apis-1.4.01.jar | grep -c 'META-INF/services' ; echo "exit=$?"
jar tf xercesImpl-2.12.2.jar | grep 'META-INF/services'
0
exit=1
META-INF/services/javax.xml.datatype.DatatypeFactory
META-INF/services/javax.xml.parsers.DocumentBuilderFactory
META-INF/services/javax.xml.parsers.SAXParserFactory
META-INF/services/javax.xml.stream.XMLEventFactory
META-INF/services/javax.xml.validation.SchemaFactory
META-INF/services/org.w3c.dom.DOMImplementationSourceList
META-INF/services/org.xml.sax.driver

对比结论:xml-apis 的 META-INF 下只有 MANIFEST.MF 一个文件(services 计数 0,grep 无命中退出码 1);配套实现 xercesImpl 2.12.2 自带 7 个 services 注册文件,JAXP 工厂正是靠这些文件找到 Xerces 实现——只引 xml-apis 不引实现 jar 时,工厂回落到 JDK 内置实现(五章实测)。

五、适用场景与限制

5.1 JDK 8 时代:endorsed 覆盖机制(历史用法实测)

JDK 8 上 org.w3c.dom、org.xml.sax、javax.xml.parsers 等接口已内置在 rt.jar,xml-apis 当年的典型用法是放进 endorsed 目录覆盖 rt.jar 的旧接口。默认状态(未放 endorsed)用 -verbose:class 观察关键类加载来源(演示程序 FactoryDemo8 见《Apache XML Commons External 在 Ubuntu 22.04 集成使用指南》五章,同一程序可直接编译复现):

/usr/lib/jvm/java-8-openjdk-amd64/bin/java -verbose:class -cp /root/factorydemo/. FactoryDemo8 2>&1 | grep '\[Loaded \(javax\.xml\.parsers\.DocumentBuilderFactory \|org\.w3c\.dom\.Node \|com\.sun\.org\.apache\.xerces\.internal\.jaxp\.DocumentBuilderFactoryImpl \)'
[Loaded javax.xml.parsers.DocumentBuilderFactory from /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar]
[Loaded com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl from /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar]
[Loaded org.w3c.dom.Node from /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar]

把 xml-apis 与 xercesImpl 同时放入 /root/endorsed 后再看:

/usr/lib/jvm/java-8-openjdk-amd64/bin/java -verbose:class -Djava.endorsed.dirs=/root/endorsed -cp /root/factorydemo/. FactoryDemo8 2>&1 | grep '\[Loaded \(javax\.xml\.parsers\.DocumentBuilderFactory \|org\.w3c\.dom\.Node \|org\.apache\.xerces\.jaxp\.DocumentBuilderFactoryImpl \)'
[Loaded javax.xml.parsers.DocumentBuilderFactory from /root/endorsed/xml-apis-1.4.01.jar]
[Loaded org.apache.xerces.jaxp.DocumentBuilderFactoryImpl from /root/endorsed/xercesImpl-2.12.2.jar]
[Loaded org.w3c.dom.Node from /root/endorsed/xml-apis-1.4.01.jar]

实测解读:endorsed 生效时接口类(DocumentBuilderFactory、Node)确实从 endorsed 目录的 xml-apis 加载、实现类从 endorsed 的 xercesImpl 加载——这就是"xml-apis + endorsed + Xerces"经典组合的底层原理。另一关键实测:endorsed 只放 xml-apis、不放 xercesImpl 会直接报错:

mkdir -p /root/endorsed_only && cp xml-apis-1.4.01.jar /root/endorsed_only/
/usr/lib/jvm/java-8-openjdk-amd64/bin/java -Djaxp.debug=1 -Djava.endorsed.dirs=/root/endorsed_only -cp /root/factorydemo/. FactoryDemo8 2>&1 | head -4
JAXP: find factoryId =javax.xml.parsers.DocumentBuilderFactory
JAXP: loaded from fallback value: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
Exception in thread "main" javax.xml.parsers.FactoryConfigurationError: Provider org.apache.xerces.jaxp.DocumentBuilderFactoryImpl not found
        at javax.xml.parsers.DocumentBuilderFactory.newInstance(Unknown Source)

报错根因正是 3.2 反编译所见:xml-apis 工厂类把 Xerces 实现写死为默认 fallback,endorse 了接口却没有配套实现,查找即失败。

💡 技术拓展:JDK 8 的 JAXP 工厂发现顺序为「系统属性 → jaxp.properties → classpath 的 META-INF/services → 默认实现」,endorsed 则在类加载层用外部 jar 顶替 rt.jar 接口类。JDK 9 模块化后 endorsed 整体移除,替代思路是「升级模块」(--upgrade-module-path)与 classpath services 注册;老的 endorsed 用法在 JDK 9+ 直接拒绝启动(5.2 实测)。

5.2 JDK 9+(实测 JDK 17):接口被 java.xml 模块遮蔽、endorsed 已移除

JDK 17 上结论完全不同:内置 java.xml 模块导出了 org.w3c.dom(含 events/ls/ranges/traversal/views/bootstrap)、org.xml.sax(含 ext)、javax.xml 全家等全部同名包(java --describe-module java.xml 可复核),classpath 上的 xml-apis 被完全遮蔽。用工厂演示程序对比两种 classpath(程序源码见集成指南第五章):

cd /root/factorydemo
java -cp . FactoryDemo
java -cp /root/xmltest/xml-apis-1.4.01.jar:. FactoryDemo
DocumentBuilderFactory impl  -> com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
SAXParserFactory impl       -> com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
API org.w3c.dom.Node module  -> java.xml
API org.xml.sax.XMLReader module -> java.xml
DocumentBuilderFactory impl  -> com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
SAXParserFactory impl       -> com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
API org.w3c.dom.Node module  -> java.xml
API org.xml.sax.XMLReader module -> java.xml

两种 classpath 输出完全一致:无论 xml-apis 是否在 classpath,Node、XMLReader 的加载模块始终是 java.xml——JDK 17 上 xml-apis 里的同名接口类是"死代码",永远不会被加载。再实测 endorsed 移除结论:

java -Djava.endorsed.dirs=/root/endorsed -cp . FactoryDemo
echo "exit=$?"
-Djava.endorsed.dirs=/root/endorsed is not supported. Endorsed standards and standalone APIs
in modular form will be supported via the concept of upgradeable modules.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
exit=1

JVM 直接拒绝启动(退出码 1)。"JDK 17 下 endorsed 已移除"为实测确认事实而非文档推测。

5.3 适用结论

  • 仍需要它的场景只剩两类:只能跑在 JDK 8 上、通过 endorsed 与 Xerces-J 严格配对的遗留工程;以及个别老库(老版 Batik/Fop 等)把 xml-apis-ext 的 SVG DOM/SAC 接口声明为编译期依赖。
  • 不需要它的场景:JDK 9 及以上的一切新工程——接口全部内置在 java.xml 模块且优先加载,引入 xml-apis 无任何效果,还背上 216KB 冗余与停更依赖的审计负担。
  • 硬性限制:只有接口没有实现(4.2 实测),单独引入无法完成解析;项目 2012 年后停更,不会有面向新 JDK 的适配更新。

六、同类选型对比

把"XML 标准接口从哪来"的四个选项放在一张表里(版本均为本次实测版本):

方案性质是否需引入实测大小适用判断
JDK 内置 java.xml 模块接口 + 内置实现(Xerces 内嵌版)无需,开箱即用随 JDKJDK 9+ 项目默认且推荐来源
xml-apis:xml-apis 1.4.01纯接口 jar(DOM/SAX/JAXP/StAX 四合一)需引入216KB / 346 类仅 JDK 8 遗留工程、与 Xerces-J 配对时
xml-apis:xml-apis 2.0.2精简接口 jar需引入107KB / 184 类老依赖声明该版本时沿用,无主动选择价值
xml-apis:xml-apis-ext 1.3.04SVG DOM + SAC + SMIL 接口需引入84KB / 192 类Batik 等图形类老项目的传递依赖
xerces:xercesImpl 2.12.2解析器实现(含 JAXP services 注册)按需引入1413KB / 962 类需指定 Xerces 版本/行为时引入,自动接管工厂

选型建议一句话:现代 JDK 项目接口一律用 JDK 内置,实现按需用 xercesImpl 的 services 机制切换;xml-apis 只会出现在存量依赖里,能排除就排除。另需区分两个名字相近的项目:本篇的 XML Commons External 是接口 jar(坐标 xml-apis),XML Commons 的另一组件 Resolver 是 Catalog 解析实体(坐标 xml-commons:xml-commons-resolver),参见《Apache XML Commons Resolver》专栏文档;集成实操全部展开在《Apache XML Commons External 在 Ubuntu 22.04 集成使用指南》。

七、参考链接