Apache XML Commons External 在 Ubuntu 22.04 集成使用指南
在 Ubuntu 22.04 上以 Maven 与直连 jar 两种方式实测引入 Apache XML Commons External(xml-apis),盘点 jar 分包构成,并用 FactoryFinder 演示与 Xerces-J 搭配时 classpath 对实现选择的影响。
Apache XML Commons External 在 Ubuntu 22.04 集成使用指南
目标:在 Ubuntu 22.04 服务器上把 Apache XML Commons External(坐标
xml-apis:xml-apis1.4.01)与解析器实现 Xerces-J(xercesImpl 2.12.2)一起引入工程,用 Maven 与直连 jar 两种方式实测依赖解析,盘点 jar 分包构成,并通过 JAXP 的 FactoryFinder 机制实测演示 classpath 上不同 jar 组合与先后顺序对"最终用哪个实现"的影响。全部命令与回显已在 Ubuntu 22.04 + OpenJDK 17 + Maven 3.9.9 实测通过。
一、环境信息
本章列出实测环境与版本核验命令,后续所有命令与回显均来自该环境。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64),JDK:OpenJDK 17.0.20.1(apt 安装 openjdk-17-jdk)
- 构建工具:Apache Maven 3.9.9(解压于
/root/apache-maven-3.9.9) - 实测组件:xml-apis 1.4.01(接口包)、xercesImpl 2.12.2(实现包)
- 实验目录:
/root/xmltest(jar 下载盘点)、/root/mvn-demo(Maven 工程)、/root/factorydemo(加载顺序演示)
版本核验(实测回显):
java -version
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)
二、前置条件
2.1 安装 JDK 17 与下载工具
Ubuntu 22.04 官方仓库直接提供 openjdk-17-jdk,安装命令为静默模式(回显退出码,已通过 java -version 验证安装成功):
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openjdk-17-jdk curl
echo "apt_exit=$?"
apt_exit=0
2.2 安装 Maven 3.9.9
仓库自带的 Maven 版本偏旧(3.6.3),推荐解压官方二进制包。实测 dlcdn 镜像已下架 3.9.9(请求返回 curl: (22) The requested URL returned error: 404),改用 archive.apache.org 归档站下载:
cd /root
curl -fsSLo maven.tgz https://archive.apache.org/dist/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz
tar xzf maven.tgz
/root/apache-maven-3.9.9/bin/mvn -v | head -3
Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: /root/apache-maven-3.9.9
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
三、依赖引入
3.1 方式A:Maven 引入(推荐)
创建演示工程 xmlapis-demo,在 pom.xml 中声明 xml-apis 1.4.01 与 xercesImpl 2.12.2 两个依赖:
mkdir -p /root/mvn-demo/src/main/java
<!-- /root/mvn-demo/pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>xmlapis-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>xml-apis</groupId>
<artifactId>xml-apis</artifactId>
<version>1.4.01</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.12.2</version>
</dependency>
</dependencies>
</project>
pom 要点:xml-apis 是接口 jar,必须与实现 jar 搭配,故同时声明 xercesImpl;编译级别设为 17 匹配本机 JDK。执行依赖树与 classpath 解析:
cd /root/mvn-demo
/root/apache-maven-3.9.9/bin/mvn -B dependency:tree | grep -E '\[INFO\] (demo|\\-|[+\`][- ])|BUILD'
[INFO] demo:xmlapis-demo:jar:1.0.0
[INFO] +- xml-apis:xml-apis:jar:1.4.01:compile
[INFO] \- xerces:xercesImpl:jar:2.12.2:compile
[INFO] BUILD SUCCESS
/root/apache-maven-3.9.9/bin/mvn -B -q dependency:build-classpath -Dmdep.outputFile=/tmp/mvncp.txt
cat /tmp/mvncp.txt
/root/.m2/repository/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar:/root/.m2/repository/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar
依赖树显示两个 jar 均以 compile 作用域解析成功,classpath 输出确认二者都进入运行时路径。
3.2 方式B:直接下载 jar
不走 Maven 的场景(离线机、临时验证)可直接从 Maven Central 取包,实测:
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 xercesImpl-2.12.2.jar $B/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar
sha256sum xml-apis-1.4.01.jar xercesImpl-2.12.2.jar
6fc991829af1708d15aea50c66f0beadcd2cfeb6968e0b2f55c1b0909883fe16 xercesImpl-2.12.2.jar
a840968176645684bb01aed376e067ab39614885f9eee44abe35a5f20ebe7fad xml-apis-1.4.01.jar
3.3 两种方式对比与建议
Maven 方式(A)自动解决传递依赖、版本可追溯,是工程首选;直接下载(B)适合离线或临时验证,需自行校验 sha256。运行期两者等价——最终都是把两个 jar 放进 classpath。
四、jar 内容盘点(实测)
4.1 xml-apis 分包统计
cd /root/xmltest
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
解读:接口集中在 org.w3c.dom、org.xml.sax 与 javax.xml 各门面包,org/apache/xmlcommons/Version 是唯一的实现工具类;没有任何解析器实现类。
4.2 xercesImpl 分包统计(对照组)
jar tf xercesImpl-2.12.2.jar | grep -c '\.class$'
jar tf xercesImpl-2.12.2.jar | grep '\.class$' | awk -F/ '{print $1"/"$2}' | sort | uniq -c | sort -rn | head -3
962
961 org/apache
1 org/w3c
解读:xercesImpl 的 962 个类几乎全部在 org.apache 下(真正的解析器实现),唯一例外是 org/w3c/dom/html/HTMLDOMImplementation(DOM HTML 扩展接口,JDK 的 java.xml 模块未提供)。
4.3 services 注册文件对比(决定实现选择的关键)
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 不带任何 services 注册文件(META-INF 下仅 MANIFEST.MF),xercesImpl 带 7 个——JAXP 工厂就是靠这些文件在 classpath 上发现 Xerces 实现的,这是五章实测的机制基础。
💡 技术拓展:JDK 17 的 JAXP 工厂(如 DocumentBuilderFactory.newInstance())查找顺序为「系统属性 → JDK 配置文件 → classpath 的 META-INF/services → JDK 内置默认实现」。决定工厂选中谁的是注册文件存在与否,而非 jar 先后顺序。
五、FactoryFinder 加载顺序实测
5.1 演示程序
写一个打印"各工厂实际选中的实现类 + 接口类由哪个模块加载"的演示程序并编译:
// /root/factorydemo/FactoryDemo.java
import javax.xml.parsers.*;
import org.w3c.dom.Node;
import org.xml.sax.XMLReader;
public class FactoryDemo {
public static void main(String[] a) throws Exception {
System.out.println("DocumentBuilderFactory impl -> " + DocumentBuilderFactory.newInstance().getClass().getName());
System.out.println("SAXParserFactory impl -> " + SAXParserFactory.newInstance().getClass().getName());
System.out.println("TransformerFactory impl -> " + javax.xml.transform.TransformerFactory.newInstance().getClass().getName());
System.out.println("API org.w3c.dom.Node module -> " + Node.class.getModule().getName());
System.out.println("API org.xml.sax.XMLReader module -> " + XMLReader.class.getModule().getName());
}
}
cd /root/factorydemo
javac FactoryDemo.java
5.2 四种 classpath 场景(实测)
四种组合依次运行(回显为实测原样,按场景分组):
java -cp . FactoryDemo
java -cp /root/xmltest/xercesImpl-2.12.2.jar:. FactoryDemo
java -cp /root/xmltest/xml-apis-1.4.01.jar:/root/xmltest/xercesImpl-2.12.2.jar:. FactoryDemo
java -cp /root/xmltest/xml-apis-1.4.01.jar:. FactoryDemo
==== 场景1:仅 JDK ====
DocumentBuilderFactory impl -> com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
SAXParserFactory impl -> com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
TransformerFactory impl -> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
API org.w3c.dom.Node module -> java.xml
API org.xml.sax.XMLReader module -> java.xml
==== 场景2:仅 xercesImpl-2.12.2.jar ====
DocumentBuilderFactory impl -> org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
SAXParserFactory impl -> org.apache.xerces.jaxp.SAXParserFactoryImpl
TransformerFactory impl -> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
API org.w3c.dom.Node module -> java.xml
API org.xml.sax.XMLReader module -> java.xml
==== 场景3:xml-apis-1.4.01.jar + xercesImpl-2.12.2.jar(Maven 组合) ====
DocumentBuilderFactory impl -> org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
SAXParserFactory impl -> org.apache.xerces.jaxp.SAXParserFactoryImpl
TransformerFactory impl -> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
API org.w3c.dom.Node module -> java.xml
API org.xml.sax.XMLReader module -> java.xml
==== 场景4:仅 xml-apis-1.4.01.jar ====
DocumentBuilderFactory impl -> com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
SAXParserFactory impl -> com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
TransformerFactory impl -> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
API org.w3c.dom.Node module -> java.xml
API org.xml.sax.XMLReader module -> java.xml
5.3 classpath 顺序与机制解读
把场景 3 的两个 jar 先后顺序交换再跑,输出仍完全一致:
java -cp /root/xmltest/xercesImpl-2.12.2.jar:/root/xmltest/xml-apis-1.4.01.jar:. FactoryDemo
java -cp /root/xmltest/xml-apis-1.4.01.jar:/root/xmltest/xercesImpl-2.12.2.jar:. FactoryDemo
DocumentBuilderFactory impl -> org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
SAXParserFactory impl -> org.apache.xerces.jaxp.SAXParserFactoryImpl
DocumentBuilderFactory impl -> org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
SAXParserFactory impl -> org.apache.xerces.jaxp.SAXParserFactoryImpl
实测结论逐条对应:
- 场景 2 与场景 3 输出一致、顺序交换后仍一致:xml-apis 与 xercesImpl 谁在 classpath 前面无关紧要,起作用的是 xercesImpl 的 META-INF/services 注册文件。
- 场景 4 与场景 1 一致:JDK 17 内置 java.xml 模块导出全部同名接口包,classpath 上的 xml-apis 接口类被遮蔽(Node/XMLReader 始终来自 java.xml 模块),且 xml-apis 无 services 也无实现,单引它不改变任何行为。
- 四个场景中 TransformerFactory 恒为 JDK 内置的 XSLTC 实现:xercesImpl 2.12.2 的 services 里没有 TransformerFactory 注册(见 4.3),XSLT 实现选择需另引 Xalan 才会变化。
💡 技术拓展:JDK 9 模块化后,org.w3c.dom / org.xml.sax / javax.xml.* 由 java.xml 模块按"父优先委托"加载,classpath jar 里的同名类永远排在模块之后——这正是现代 JDK 不必引入 xml-apis 的根本原因(选型分析与 JDK 8 endorsed 历史对照见《Apache XML Commons External 介绍与选型指南》)。
六、集成示例:解析 XML(可独立执行)
在 Maven 工程里写一个真实解析示例,把 XML 字符串解析成 DOM 树并遍历:
// /root/mvn-demo/src/main/java/XmlParseDemo.java
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.ByteArrayInputStream;
public class XmlParseDemo {
public static void main(String[] args) throws Exception {
String xml = "<users><user id=\"1\"><name>quzuo</name></user>"
+ "<user id=\"2\"><name>admin</name></user></users>";
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
NodeList list = doc.getElementsByTagName("user");
System.out.println("解析器实现: " + factory.getClass().getName());
System.out.println("user 节点数: " + list.getLength());
for (int i = 0; i < list.getLength(); i++) {
Element e = (Element) list.item(i);
System.out.println("user[" + i + "] id=" + e.getAttribute("id")
+ " name=" + e.getTextContent());
}
}
}
export LANG=C.UTF-8
cd /root/mvn-demo
/root/apache-maven-3.9.9/bin/mvn -B -q compile ; echo "compile_exit=$?"
java -cp "$(cat /tmp/mvncp.txt):target/classes" XmlParseDemo
compile_exit=0
解析器实现: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
user 节点数: 2
user[0] id=1 name=quzuo
user[1] id=2 name=admin
把两个依赖 jar 从 classpath 拿掉、代码零改动再跑(java -cp "target/classes" XmlParseDemo),实测解析仍成功,仅"解析器实现"一行变为 com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl(JDK 内置实现)。差别只在工厂选中谁:classpath 上有 xercesImpl 时走 Xerces-J 的 JAXP 适配层,没有时走 JDK 内置实现——这正是 JAXP 工厂机制的价值,也是该组合在现代 JDK 上的实际效果:接口来自模块、实现由 services 切换。
七、注意事项
- JDK 17 上不要用 endorsed 机制,实测 JVM 直接拒绝启动:
java -Djava.endorsed.dirs=/root/xmltest -cp /root/factorydemo/. FactoryDemo
echo "exit=$?"
-Djava.endorsed.dirs=/root/xmltest 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
- JDK 8 时代若必须用 endorsed 覆盖接口,xml-apis 与 xercesImpl 必须同时放入 endorsed 目录:只放 xml-apis 会因工厂内置 fallback 指向 Xerces 而抛 FactoryConfigurationError(实测对照记录在《Apache XML Commons External 介绍与选型指南》5.1)。
- xml-apis 1.4.01 无 META-INF/services、无实现类,单独引入不改变工厂行为;版本认准 Maven Central 实测存在的版本(1.4.01/2.0.2 等,无 1.4.02),项目 2012 年后停更,新工程直接用 JDK 内置接口。TransformerFactory 与 DatatypeFactory 等门面不受 xercesImpl 影响(services 未覆盖),如需切换转换器需另引 Xalan 等实现。
八、参考链接
- Apache XML Commons 项目主页:https://xerces.apache.org/xml-commons/
- Maven Central 坐标页(xml-apis):https://repo1.maven.org/maven2/xml-apis/xml-apis/
- Maven Central 坐标页(xercesImpl):https://repo1.maven.org/maven2/xerces/xercesImpl/
- Apache Maven 安装归档:https://archive.apache.org/dist/maven/maven-3/