Ubuntu 22.04 安装 Apache Xerces 指南

面向 Ubuntu 22.04 的 Apache Xerces 部署指南:安装 OpenJDK 17 与 Maven,引入 xercesImpl 2.12.2,实测 DOM、SAX、XSD 校验与 JAXP 切换,含堆参数配置与卸载方法。

Apache XercesApache Xerces2026/10/12

Ubuntu 22.04 安装 Apache Xerces 指南

一、环境信息

1.1 服务器与软件版本

本文全部步骤均在 Ubuntu 22.04 服务器上实测完成,版本信息如下:

项目实测版本
操作系统Ubuntu 22.04.5 LTS(Jammy,x86_64)
JDKOpenJDK 17.0.20.1(openjdk-17-jdk 17.0.20.1+1-1~22.04)
构建工具Apache Maven 3.6.3(3.6.3-5)
Apache XercesXerces-J 2.12.2(xerces:xercesImpl,Maven Central 实测最新版)
传递依赖xml-apis 1.4.01(随 xercesImpl 自动引入)

1.2 账号说明

本文所有命令均以 root 账号执行(演示账号 root,密码 q1w2e3r4)。普通用户执行 apt-get install 时需加 sudo。

1.3 软件源说明

使用系统默认的 Ubuntu 官方源即可,apt-get update 实测正常。无特殊网络要求,仅需保证能访问 Maven Central(repo.maven.apache.org)。

二、前置条件

2.1 资源要求

  • CPU:2 核即可(实测构建与解析均在秒级完成);
  • 内存:不低于 2 GB(实测解析 38.2 MB XML 时 DOM 峰值堆占用约 456 MB,JVM 堆上限按 512 MB 配置);
  • 磁盘:不低于 5 GB(openjdk-17-jdk 与 Maven 安装后约 1 GB,本地 Maven 仓库约 300 MB,余量留给工程);
  • 网络:能访问 archive.ubuntu.com 与 Maven Central。

2.2 刷新软件源索引

apt-get update

实测尾部回显:

All packages are up to date.

(首次执行会显示各源 Hit/Get 行,只要最后无 Err 即可。)

2.3 检查既有 Java 环境

java -version

实测全新服务器回显:

bash: java: command not found

若已安装 OpenJDK 11(/usr/lib/jvm 下会同时出现 java-1.11.0 与 java-1.17.0 目录),apt-get install openjdk-17-jdk 会通过 update-alternatives 自动把默认 java 指向 17,无需手动切换;安装后务必按 4.1 节复核。

三、版本选型

3.1 Xerces-J 与 JDK 内置 Xerces 的关系与差异

Java 生态中的 "Xerces" 有两套并存的实现:

对比项JDK 内置实现Xerces-J 独立版
包名前缀com.sun.org.apache.xerces.internal.*org.apache.xerces.*
发布渠道随 JDK 发布,不可单独升级Maven Central 独立发布(实测最新 2.12.2,发布于 2022-01-27)
JAXP 工厂类com.sun.org.apache.xerces.internal.jaxp.*org.apache.xerces.jaxp.*
缺陷修复取决于 JDK 版本节奏社区独立修复,但 2.12.2 后已无新版本
附加能力无原生解析器类提供 org.apache.xerces.parsers.DOMParser/SAXParser 原生类与扩展特性集

两者都实现 JAXP 标准接口(javax.xml.parsers.*、javax.xml.validation.*),业务代码面向接口编写即可无缝切换。是否引入独立版,取决于是否需要 Xerces 扩展特性、扩展属性或原生解析器类。

3.2 版本获取渠道

  • Maven 坐标:xerces:xercesImpl:2.12.2(Maven Central 实测 latestVersion 为 2.12.2);
  • 官方二进制包:Xerces-J-bin.2.12.2.tar.gz,实测从 archive.apache.org 下载,大小 5,648,672 字节,SHA-256 为 1ad48949b2c7f0df91668f058d6ec773871adb5527b0b5c73dc2160ab5d162e2。

四、部署步骤

4.1 安装 OpenJDK 17

apt-get install -y openjdk-17-jdk

实测尾部回显:

Processing triggers for libgdk-pixbuf-2.0-0:amd64 (2.42.8+dfsg-1ubuntu0.5) ...

验证:

# 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)
# javac -version
javac 17.0.20.1

4.2 安装 Maven

apt-get install -y maven

验证:

# mvn -version
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "6.8.0-138-generic", arch: "amd64", family: "unix"

4.3 创建 Maven 工程

mkdir -p /root/xerces-demo/src/main/java/com/example/xerces
mkdir -p /root/xerces-demo/src/main/resources
cd /root/xerces-demo

目录结构:

/root/xerces-demo
├── pom.xml
└── src/main
    ├── java/com/example/xerces
    └── resources

编写 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>com.example</groupId>
    <artifactId>xerces-demo</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>2.12.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
            </plugin>
        </plugins>
    </build>
</project>

4.4 引入 xercesImpl 依赖并构建

cd /root/xerces-demo
mvn -q package

构建成功后确认本地仓库中的依赖文件:

# ls /root/.m2/repository/xerces/xercesImpl/2.12.2/
_remote.repositories
xercesImpl-2.12.2.jar
xercesImpl-2.12.2.jar.sha1
xercesImpl-2.12.2.pom
xercesImpl-2.12.2.pom.sha1

导出完整运行时 classpath(可见 xercesImpl 自动带入 xml-apis 1.4.01):

mvn -q dependency:build-classpath -Dmdep.outputFile=/tmp/cp.txt
cat /tmp/cp.txt

实测回显:

/root/xerces-demo/target/classes:/root/.m2/repository/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar:/root/.m2/repository/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar

4.5 准备样例 XML 与 XSD

src/main/resources/books.xml(合法样例):

<?xml version="1.0" encoding="UTF-8"?>
<books xmlns="http://www.example.com/books">
    <book id="101">
        <title>Java核心技术</title>
        <author>周志明</author>
        <price>129.00</price>
    </book>
    <book id="102">
        <title>Effective Java</title>
        <author>Joshua Bloch</author>
        <price>108.50</price>
    </book>
</books>

src/main/resources/books.xsd(Schema 定义):

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:tns="http://www.example.com/books"
           targetNamespace="http://www.example.com/books"
           elementFormDefault="qualified">

    <xs:element name="books" type="tns:BooksType"/>

    <xs:complexType name="BooksType">
        <xs:sequence>
            <xs:element name="book" type="tns:BookType" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="BookType">
        <xs:sequence>
            <xs:element name="title" type="xs:string"/>
            <xs:element name="author" type="xs:string"/>
            <xs:element name="price" type="xs:decimal"/>
        </xs:sequence>
        <xs:attribute name="id" type="xs:int" use="required"/>
    </xs:complexType>
</xs:schema>

src/main/resources/books-invalid.xml(违反 Schema:缺 id 属性、price 不是数字):

<?xml version="1.0" encoding="UTF-8"?>
<books xmlns="http://www.example.com/books">
    <book id="101">
        <title>Java核心技术</title>
        <author>周志明</author>
        <price>129.00</price>
    </book>
    <book>
        <title>Effective Java</title>
        <author>Joshua Bloch</author>
        <price>not-a-number</price>
    </book>
</books>

src/main/resources/books-badwf.xml(格式不合法:根元素未闭合):

<?xml version="1.0" encoding="UTF-8"?>
<!-- 故意制造标签未闭合的格式错误,用于演示解析器致命错误 -->
<books xmlns="http://www.example.com/books">
    <book id="101">
        <title>Java核心技术</title>
        <author>周志明</author>
        <price>129.00</price>
    </book>

4.6 DOM 解析验证

编写 src/main/java/com/example/xerces/DomDemo.java:

package com.example.xerces;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;

public class DomDemo {
    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        System.out.println("DocumentBuilderFactory 实现: " + factory.getClass().getName());
        factory.setNamespaceAware(true); // 默认 false,关闭时按命名空间查找将得到 0 个节点

        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(new File("src/main/resources/books.xml"));

        Element root = doc.getDocumentElement();
        System.out.println("根元素: " + root.getTagName());

        NodeList books = root.getElementsByTagNameNS("*", "book");
        System.out.println("book 节点数: " + books.getLength());
        for (int i = 0; i < books.getLength(); i++) {
            Element book = (Element) books.item(i);
            String id = book.getAttribute("id");
            String title = book.getElementsByTagNameNS("*", "title").item(0).getTextContent();
            String author = book.getElementsByTagNameNS("*", "author").item(0).getTextContent();
            String price = book.getElementsByTagNameNS("*", "price").item(0).getTextContent();
            System.out.println("book[" + i + "] id=" + id + " | " + title + " | " + author + " | " + price);
        }
    }
}

编译并运行(注意携带 UTF-8 locale,否则中文输出会变成问号):

export LC_ALL=C.UTF-8
java -cp target/classes:$(cat /tmp/cp.txt) com.example.xerces.DomDemo

实测回显:

DocumentBuilderFactory 实现: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
根元素: books
book 节点数: 2
book[0] id=101 | Java核心技术 | 周志明 | 129.00
book[1] id=102 | Effective Java | Joshua Bloch | 108.50

4.7 SAX 事件流解析验证

编写 src/main/java/com/example/xerces/SaxDemo.java:

package com.example.xerces;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.XMLReader;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

public class SaxDemo {
    public static void main(String[] args) throws Exception {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        // 传入参数 ns 开启命名空间感知;默认 false,此时 uri 与 localName 均为空串
        if (args.length > 0 && "ns".equals(args[0])) {
            factory.setNamespaceAware(true);
        }
        System.out.println("SAXParserFactory 实现: " + factory.getClass().getName()
                + " (namespaceAware=" + factory.isNamespaceAware() + ")");

        SAXParser parser = factory.newSAXParser();
        XMLReader reader = parser.getXMLReader();

        reader.setContentHandler(new ContentHandler() {
            @Override
            public void setDocumentLocator(Locator locator) {
                System.out.println("[setDocumentLocator] systemId=" + locator.getSystemId());
            }

            @Override
            public void startDocument() {
                System.out.println("[startDocument]");
            }

            @Override
            public void endDocument() {
                System.out.println("[endDocument]");
            }

            @Override
            public void startPrefixMapping(String prefix, String uri) {
            }

            @Override
            public void endPrefixMapping(String prefix) {
            }

            @Override
            public void startElement(String uri, String localName, String qName, Attributes atts) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < atts.getLength(); i++) {
                    sb.append(" ").append(atts.getQName(i)).append("=").append(atts.getValue(i));
                }
                System.out.println("[startElement] uri=" + uri + " localName=" + localName + " qName=" + qName + sb);
            }

            @Override
            public void endElement(String uri, String localName, String qName) {
                System.out.println("[endElement] " + qName);
            }

            @Override
            public void characters(char[] ch, int start, int length) {
                String text = new String(ch, start, length).trim();
                if (!text.isEmpty()) {
                    System.out.println("[characters] " + text);
                }
            }

            @Override
            public void ignorableWhitespace(char[] ch, int start, int length) {
            }

            @Override
            public void processingInstruction(String target, String data) {
            }

            @Override
            public void skippedEntity(String name) {
            }
        });

        reader.parse("src/main/resources/books.xml");
    }
}

运行(命名空间感知模式,截取开头片段):

java -cp target/classes:$(cat /tmp/cp.txt) com.example.xerces.SaxDemo ns

实测回显:

SAXParserFactory 实现: org.apache.xerces.jaxp.SAXParserFactoryImpl (namespaceAware=true)
[setDocumentLocator] systemId=file:///root/xerces-demo/src/main/resources/books.xml
[startDocument]
[startElement] uri=http://www.example.com/books localName=books qName=books
[startElement] uri=http://www.example.com/books localName=book qName=book id=101
[startElement] uri=http://www.example.com/books localName=title qName=title
[characters] Java核心技术
[endElement] title
...
[endElement] books
[endDocument]

4.8 XSD Schema 校验验证

编写 src/main/java/com/example/xerces/ValidateDemo.java:

package com.example.xerces;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.File;

public class ValidateDemo {
    public static void main(String[] args) throws Exception {
        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        System.out.println("SchemaFactory 实现: " + schemaFactory.getClass().getName());

        Schema schema = schemaFactory.newSchema(new File("src/main/resources/books.xsd"));
        Validator validator = schema.newValidator();

        String[] files = {
                "src/main/resources/books.xml",
                "src/main/resources/books-invalid.xml",
                "src/main/resources/books-badwf.xml"
        };

        for (String file : files) {
            System.out.println("==== 校验 " + file + " ====");
            validator.reset();
            final int[] errors = {0}; // error() 回调不抛异常时校验器会继续跑完,须自行计数
            validator.setErrorHandler(new ErrorHandler() {
                @Override
                public void warning(SAXParseException e) {
                    System.out.println("[WARNING]  行 " + e.getLineNumber() + " 列 " + e.getColumnNumber() + ": " + e.getMessage());
                }

                @Override
                public void error(SAXParseException e) {
                    errors[0]++;
                    System.out.println("[ERROR]    行 " + e.getLineNumber() + " 列 " + e.getColumnNumber() + ": " + e.getMessage());
                }

                @Override
                public void fatalError(SAXParseException e) throws SAXParseException {
                    errors[0]++;
                    System.out.println("[FATAL]    行 " + e.getLineNumber() + " 列 " + e.getColumnNumber() + ": " + e.getMessage());
                    throw e;
                }
            });
            try {
                validator.validate(new StreamSource(new File(file)));
                if (errors[0] == 0) {
                    System.out.println("结果: 校验通过");
                } else {
                    System.out.println("结果: 校验失败,共报告 " + errors[0] + " 处错误");
                }
            } catch (SAXParseException e) {
                System.out.println("结果: 校验失败(致命错误已中断解析)");
            }
        }
    }
}

运行:

java -cp target/classes:$(cat /tmp/cp.txt) com.example.xerces.ValidateDemo

实测回显(合法文件通过、违规文件报出 cvc 错误码与行列号、格式错误文件报致命错误):

SchemaFactory 实现: org.apache.xerces.jaxp.validation.XMLSchemaFactory
==== 校验 src/main/resources/books.xml ====
结果: 校验通过
==== 校验 src/main/resources/books-invalid.xml ====
[ERROR]    行 8 列 11: cvc-complex-type.4: Attribute 'id' must appear on element 'book'.
[ERROR]    行 11 列 36: cvc-datatype-valid.1.2.1: 'not-a-number' is not a valid value for 'decimal'.
[ERROR]    行 11 列 36: cvc-type.3.1.3: The value 'not-a-number' of element 'price' is not valid.
结果: 校验失败,共报告 3 处错误
==== 校验 src/main/resources/books-badwf.xml ====
[FATAL]    行 9 列 1: XML document structures must start and end within the same entity.
结果: 校验失败(致命错误已中断解析)

4.9 JAXP 实现切换验证

编写 src/main/java/com/example/xerces/JaxpSwitchDemo.java:

package com.example.xerces;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParserFactory;

public class JaxpSwitchDemo {
    public static void main(String[] args) throws Exception {
        System.out.println("javax.xml.parsers.DocumentBuilderFactory = "
                + System.getProperty("javax.xml.parsers.DocumentBuilderFactory", "(未设置)"));
        System.out.println("实际 DocumentBuilderFactory 实现: "
                + DocumentBuilderFactory.newInstance().getClass().getName());
        System.out.println("javax.xml.parsers.SAXParserFactory = "
                + System.getProperty("javax.xml.parsers.SAXParserFactory", "(未设置)"));
        System.out.println("实际 SAXParserFactory 实现: "
                + SAXParserFactory.newInstance().getClass().getName());
    }
}

三种场景实测:

# 场景 1:classpath 不含 xercesImpl,使用 JDK 内置实现
java -cp target/classes com.example.xerces.JaxpSwitchDemo

# 场景 2:classpath 含 xercesImpl,观察自动切换
java -cp target/classes:$(cat /tmp/cp.txt) com.example.xerces.JaxpSwitchDemo

# 场景 3:显式用系统属性指定 Xerces 实现
java -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl \
     -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl \
     -cp target/classes com.example.xerces.JaxpSwitchDemo

实测回显(依次为场景 1、2、3):

场景 1:
javax.xml.parsers.DocumentBuilderFactory = (未设置)
实际 DocumentBuilderFactory 实现: com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
javax.xml.parsers.SAXParserFactory = (未设置)
实际 SAXParserFactory 实现: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl

场景 2:
javax.xml.parsers.DocumentBuilderFactory = (未设置)
实际 DocumentBuilderFactory 实现: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
javax.xml.parsers.SAXParserFactory = (未设置)
实际 SAXParserFactory 实现: org.apache.xerces.jaxp.SAXParserFactoryImpl

场景 3:
javax.xml.parsers.DocumentBuilderFactory = org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
实际 DocumentBuilderFactory 实现: org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
javax.xml.parsers.SAXParserFactory = org.apache.xerces.jaxp.SAXParserFactoryImpl
实际 SAXParserFactory 实现: org.apache.xerces.jaxp.SAXParserFactoryImpl

五、配置说明

5.1 JVM 堆参数

XML 解析对堆敏感,建议统一按 256–512 MB 配置。修改位置为 /etc/profile.d/xerces-maven.sh(新建文件,登录 shell 全局生效):

echo 'export MAVEN_OPTS="-Xms256m -Xmx512m"' > /etc/profile.d/xerces-maven.sh

注意两点:

  1. 不要写在 /root/.bashrc 里:非交互 shell 会因 bashrc 开头的交互检测而提前 return,实测 source 后 MAVEN_OPTS 仍为空;
  2. Maven 3.6.3 的 mvn -version 不会打印 MAVEN_OPTS,需要用进程工具验证。

验证方式一:后台启动解析进程后用 jps 查看实际生效的 JVM 参数(实测回显):

# jps -lv
7857 com.example.xerces.BigXmlDom -Xms256m -Xmx512m
7910 jdk.jcmd/sun.tools.jps.Jps -Dapplication.home=/usr/lib/jvm/java-17-openjdk-amd64 -Xms8m -Djdk.module.main=jdk.jcmd

验证方式二:ps -ef | grep [j]ava 查看完整命令行(实测回显):

root      7857  7848 99 14:46 ?        00:00:06 java -Xms256m -Xmx512m -cp target/classes:/root/.m2/repository/xerces/xercesImpl/2.12.2/xercesImpl-2.12.2.jar:/root/.m2/repository/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar com.example.xerces.BigXmlDom /root/xerces-demo/big-books.xml 20

直接用 java 命令运行时,把堆参数写进命令行即可,也可通过 JAVA_OPTS 变量传递:

export JAVA_OPTS="-Xms256m -Xmx512m"
java $JAVA_OPTS -cp target/classes:$(cat /tmp/cp.txt) com.example.xerces.DomDemo

5.2 字符集与 locale

Ubuntu 22.04 默认 locale 可能是 POSIX,程序中的中文会输出为问号(实测输出形如 ????Java???)。确认并设置:

locale -a | grep -i utf
echo 'export LC_ALL=C.UTF-8' > /etc/profile.d/lang.sh

实测本机存在 C.utf8,直接引用 C.UTF-8 即可生效。

5.3 Maven 相关

  • 本地仓库位置:/root/.m2/repository;
  • 远程仓库:默认中央仓库直连可用,实测可正常下载 xercesImpl 2.12.2;
  • 全局配置文件:/usr/share/maven/conf/settings.xml,用户级为 ~/.m2/settings.xml。

六、升级与多版本

6.1 Maven 依赖升级

修改 pom.xml 中 xercesImpl 的 <version> 后重新执行 mvn -q package,新版本 jar 会进入本地仓库对应目录,旧版本目录保留,互不影响。

6.2 官方二进制包多版本共存

从 archive.apache.org 下载不同版本的 Xerces-J-bin.x.y.z.tar.gz,分别解压到 /opt/xerces-2_12_2、/opt/xerces-2_11_0 等独立目录,运行时用 -cp 显式指定目录内 jar:

java -cp /opt/xerces-2_12_2/xercesSamples.jar:/opt/xerces-2_12_2/xercesImpl.jar:/opt/xerces-2_12_2/xml-apis.jar:/opt/xerces-2_12_2/serializer.jar:/opt/xerces-2_12_2/samples/xs sax.Counter /opt/xerces-2_12_2/data/personal.xml

6.3 实现选择优先级

JAXP 选择实现的优先级(实测结论):系统属性 -D 最高;其次是 classpath 中 jar 包内 META-INF/services 声明(ServiceLoader);都没有时回落到 JDK 内置实现。因此"是否升级 xercesImpl"不会影响未引入该依赖的应用。

七、注意事项

  1. 引入 xercesImpl 后 JAXP 默认实现被静默接管。 xercesImpl-2.12.2.jar 内含 META-INF/services/javax.xml.parsers.DocumentBuilderFactory 等声明文件,只要出现在 classpath,DocumentBuilderFactory.newInstance() 就会返回 org.apache.xerces.jaxp.DocumentBuilderFactoryImpl(实测)。混用多个 XML 库时需注意行为差异。
  2. 解析器默认不感知命名空间。 DocumentBuilderFactory 与 SAXParserFactory 的 namespaceAware 默认均为 false,此时 getElementsByTagNameNS 返回 0 个节点、SAX 回调的 uri/localName 为空串(实测)。需要命名空间时必须显式 setNamespaceAware(true)。
  3. ErrorHandler 的 error() 不抛异常时校验会"照常通过"。 实测 error 回调只打印不抛出时,validator.validate() 正常返回,结论必须结合错误计数判断。
  4. jcmd/jinfo 可能不可用。 实测对运行中的 JVM 执行 jcmd VM.flags、jinfo -flag 均报 Unable to open socket file /tmp/.java_pidXXXX: target process doesn't respond within 10500ms or HotSpot VM not loaded,此时改用 jps -lv、jstat -gcutil 验证 JVM 参数与 GC 状态。
  5. apt 版本随源更新。 openjdk-17-jdk 的候选版本随 Ubuntu 补丁更新(实测 17.0.20.1+1-1~22.04),apt-cache policy openjdk-17-jdk 可确认实际安装版本。
  6. locale 问题优先排查。 输出中文变问号时先用 locale 命令确认当前值,再看 locale -a 中可用的 UTF-8 项。

八、常见问题

Q1:apt-get install 报 404 或 Hash Sum mismatch? 先执行 apt-get update 刷新索引;仍失败时检查 /etc/apt/sources.list 是否被改为失效的第三方地址,恢复官方源后重试。

Q2:程序运行时中文全部显示为问号(如 ????Java???)? 系统 locale 为 POSIX 所致。执行 export LC_ALL=C.UTF-8(建议写入 /etc/profile.d/lang.sh),重新登录 shell 后生效。

Q3:getElementsByTagNameNS("*", "book") 返回 0 个节点? 解析器默认不感知命名空间。在 DocumentBuilderFactory.newInstance() 之后调用 factory.setNamespaceAware(true) 再创建 DocumentBuilder(实测默认关闭时返回 0,开启后返回 2)。

Q4:把 -Xmx 调到 256m 后大文件解析报错? 实测 38.2 MB XML 用 DOM 全量加载,堆峰值约 456 MB。堆上限 256 MB 时实测报 An exception occurred while executing the Java class. Java heap space。建议按第五节配置 512 MB,或改用 SAX 流式解析。

Q5:mvn package 时报 source option 8 is obsolete 警告? JDK 17 编译 target 8 的过时提示,不影响构建产物,可忽略;如需消除可把 maven.compiler.source/target 提升到与运行时一致的版本。

Q6:安装 openjdk-17-jdk 时自动装了 OpenJDK 11? openjdk-17-jdk 依赖链可能带出 11 号 JDK 作为依赖(/usr/lib/jvm 下可见 java-1.11.0 目录),属正常现象;update-alternatives 已把默认 java 指向 17,用 java -version 复核即可。

九、卸载方式

按依赖顺序回退(先删应用数据,再删组件包):

rm -rf /root/xerces-demo
rm -rf /root/.m2
rm -f /etc/profile.d/xerces-maven.sh /etc/profile.d/lang.sh
rm -rf /opt/xerces-2_12_2 /opt/Xerces-J-bin.2.12.2.tar.gz
apt-get remove -y openjdk-17-jdk maven

如需连依赖与配置一并清理,可再执行 apt-get purge -y openjdk-17-jdk maven && apt-get autoremove -y。

卸载后验证:

java -version
mvn -version

两者均应提示 command not found。

十、参考链接