CentOS 8 安装 Apache Xerces 指南

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

Apache XercesApache Xerces2026/10/12

CentOS 8 安装 Apache Xerces 指南

一、环境信息

1.1 服务器与软件版本

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

项目实测版本
操作系统CentOS Linux 8(8.5.2111,x86_64)
JDKOpenJDK 17.0.1(java-17-openjdk-17.0.1.0.12-2.el8_5)
构建工具Apache Maven 3.5.4(Red Hat 3.5.4-5)
Apache XercesXerces-J 2.12.2(xerces:xercesImpl,Maven Central 实测最新版)
传递依赖xml-apis 1.4.01(随 xercesImpl 自动引入)

1.2 账号说明

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

1.3 软件源说明

CentOS 8 已停止维护,官方在线源已下线。实测服务器将软件源指向了 vault 归档仓库,/etc/yum.repos.d/ 中的实际配置为:

/etc/yum.repos.d/CentOS-Linux-AppStream.repo:baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/AppStream/x86_64/os/
/etc/yum.repos.d/CentOS-Linux-BaseOS.repo:baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/BaseOS/x86_64/os/

若服务器仍指向已失效的官方源,请先按 2.2 节处理,否则后续安装会失败。

二、前置条件

2.1 资源要求

  • CPU:2 核即可(实测 Maven 构建与解析均在秒级完成);
  • 内存:不低于 2 GB(实测解析 38.2 MB XML 时 DOM 峰值堆占用约 462 MB,JVM 堆上限按 512 MB 配置);
  • 磁盘:不低于 5 GB(JDK、Maven、本地仓库合计约 1.5 GB,余量留给工程与依赖缓存);
  • 网络:能访问 Maven Central(repo.maven.apache.org),用于下载 xercesImpl。

2.2 检查并修复软件源

查看当前源指向:

grep -rE '^(baseurl|mirrorlist|metalink)' /etc/yum.repos.d/

如 mirrorlist 指向已下线的官方地址,将其注释并改为 vault 归档源:

sed -i 's|^mirrorlist=|#mirrorlist=|' /etc/yum.repos.d/CentOS-Linux-*.repo
sed -i 's|^#baseurl=http://mirror.centos.org|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault|' /etc/yum.repos.d/CentOS-Linux-*.repo

刷新缓存,实测回显:

# dnf makecache
CentOS Linux 8 - AppStream                       21 kB/s | 4.3 kB     00:00
CentOS Linux 8 - BaseOS                          19 kB/s | 3.9 kB     00:00
Metadata cache created.

2.3 检查既有 Java 环境

java -version

实测新机回显:

bash: java: command not found

若已安装其他版本(如 OpenJDK 8),dnf install java-17-openjdk-devel 不会自动切换默认 java 命令,需按 4.1 节用 alternatives 显式切换。

三、版本选型

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

dnf install -y java-17-openjdk-devel

实测安装尾部回显:

  which-2.21-16.el8.x86_64
  xorg-x11-font-utils-1:7.5-41.el8.x86_64
  xorg-x11-fonts-Type1-7.5-19.el8.noarch

Complete!

实测服务器此前已装有 OpenJDK 8,系统默认 java 仍指向 1.8,用 alternatives 显式切换(每个命令只操作一个目标):

alternatives --set java /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/java
alternatives --set javac /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/javac

验证:

# java -version
openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM (build 17.0.1+12-LTS, mixed mode, sharing)
# javac -version
javac 17.0.1

4.2 安装 Maven

dnf install -y maven

验证:

# mvn -version
Apache Maven 3.5.4 (Red Hat 3.5.4-5)
Maven home: /usr/share/maven
Java version: 17.0.1, vendor: Oracle Corporation, runtime: /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "4.18.0-348.el8.x86_64", 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

第一行即证明 JAXP 工厂已自动切换为 Xerces 独立版实现(原因见 3.1 节与 7 章注意事项)。

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 /root/.bashrc 后 MAVEN_OPTS 仍为空;
  2. Maven 3.5.4 的 mvn -version 不会打印 MAVEN_OPTS,需要用进程工具验证。

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

# jps -lv
1483 com.example.xerces.BigXmlDom -Xms256m -Xmx512m
1532 jdk.jcmd/sun.tools.jps.Jps -Dapplication.home=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64 -Xms8m -Djdk.module.main=jdk.jcmd

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

root     1483  1475 99 14:53 ?        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

系统默认 locale 为 POSIX 时,程序中的中文会输出为问号(实测输出形如 ????Java???)。统一设置:

echo 'export LC_ALL=C.UTF-8' > /etc/profile.d/lang.sh

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 会进入本地仓库对应目录(如 xerces/xercesImpl/2.13.0/),旧版本目录保留,互不影响。

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. alternatives 不会自动切换。 已有 OpenJDK 8 的机器安装 java-17-openjdk-devel 后默认 java 仍是 1.8,必须按 4.1 节显式 alternatives --set。
  6. CentOS 8 源失效问题。 官方源已下线,任何 dnf install 前先确认指向 vault 归档仓库。

八、常见问题

Q1:执行 dnf install 报 "Failed to download metadata for repo 'AppStream'" 怎么办? CentOS 8 官方源已停止服务。按 2.2 节把 /etc/yum.repos.d/CentOS-Linux-*.repo 改为 vault 归档地址(实测使用 https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/),再执行 dnf makecache。

Q2:程序运行时中文全部显示为问号(如 ????Java???)? 系统 locale 为 POSIX 所致。执行 locale -a 确认存在 C.utf8,然后设置 export LC_ALL=C.UTF-8(建议写入 /etc/profile.d/lang.sh)。

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

Q4:把 -Xmx 调到 256m 后大文件解析报错? 实测 38.2 MB XML 用 DOM 全量加载,堆峰值约 455–462 MB。堆上限 256 MB 时实测报 An exception occurred while executing the Java class. GC overhead limit exceeded(JDK 17.0.1 下也可能报 Java heap space)。建议按第五节配置 512 MB,或改用 SAX 流式解析。

Q5:安装了 java-17-openjdk-devel,java -version 仍显示 1.8? 多版本共存时 alternatives 未切换。执行 4.1 节的两条 alternatives --set 命令后重新登录 shell 再验证。

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

九、卸载方式

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

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
dnf remove -y java-17-openjdk-devel maven

卸载后验证:

java -version
mvn -version

两者均应提示 command not found(若系统还有其他 JDK 版本,java 命令会回落到 alternatives 中的剩余条目)。

十、参考链接