CentOS 8 安装 Apache Xalan 指南
在 CentOS 8 从零到一安装 Apache Xalan-J 2.7.3:装 OpenJDK 17 与 Maven、建工程引依赖,命令行与 API 双路径实测,含版本选型、升级卸载与常见问题。
CentOS 8 安装 Apache Xalan 指南
目标:在 CentOS 8 服务器上从零到一安装 Apache Xalan-J(XSLT 处理器,本文以 Java 版 2.7.3 为准),覆盖前置 JDK/Maven、Maven 工程引依赖、命令行 Process 与 Java Transformer 两条转换路径的验证、JVM 堆设置、升级/多版本管理与卸载。本文全部命令与回显已在 CentOS Linux 8.5.2111 + OpenJDK 17.0.1 真实环境逐条实测通过。
一、环境信息
| 项目 | 值 |
|---|---|
| 操作系统 | CentOS Linux 8.5.2111(x86_64) |
| 软件源 | BaseOS / AppStream(vault 仓库) |
| JDK | OpenJDK 17.0.1(java-17-openjdk-17.0.1.0.12-2.el8_5) |
| Maven | 3.5.4(Red Hat 3.5.4-5) |
| Xalan-J | xalan:xalan:2.7.3 + xalan:serializer:2.7.3(Maven Central 实测 release 版) |
| 示例工程目录 | /opt/xalan-demo |
| 管理账号 | root(密码 q1w2e3r4) |
二、前置条件
- root 权限(本文全部以 root 执行)。
- 服务器可访问 yum 仓库与 Maven 中央仓库
repo.maven.apache.org。 - Xalan-J 运行依赖 JVM:需 JDK 8 及以上(本文实测 OpenJDK 17,向下兼容 8/11)。
确认网络与仓库可用:
dnf repoinfo | grep -E "repo-id|baseurl"
✅ 实测回显:
Repo-baseurl : https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/AppStream/x86_64/os/ Repo-baseurl : https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.5.2111/BaseOS/x86_64/os/
三、版本选型说明
3.1 Xalan-J 版本现状
Xalan-J 通过 Maven Central 分发(CentOS 8 官方仓库无 xalan RPM 包)。实测 https://repo1.maven.org/maven2/xalan/xalan/maven-metadata.xml:
- 最新 release:2.7.3(lastUpdated 2023-05-19),配套
xalan:serializer:2.7.3; - 历史版本线:2.1.0 ~ 2.7.3,其中 2.7.2、2.7.3 为常用稳定版;
- 选型建议:新部署直接用 2.7.3(含多年累积修复,且对高版本 JDK 兼容性最好)。
3.2 Xalan-J 与 JDK/JAXP 的关系
Xalan-J 是 JAXP(javax.xml.transform)标准接口的一组实现,不是独立进程,而是一组 jar:
| jar | 作用 | 关键类 |
|---|---|---|
| xalan-2.7.3.jar | XSLT 引擎(解释型 + XSLTC 编译型) | org.apache.xalan.xslt.Process(命令行)、org.apache.xalan.processor.TransformerFactoryImpl(解释型工厂)、org.apache.xalan.xsltc.trax.TransformerFactoryImpl(XSLTC 工厂) |
| serializer-2.7.3.jar | 结果序列化输出 | org.apache.xml.serializer.* |
实测关键点:xalan-2.7.3.jar 内含 META-INF/services/javax.xml.transform.TransformerFactory,内容为 org.apache.xalan.processor.TransformerFactoryImpl。即只要 xalan.jar 出现在 classpath,TransformerFactory.newInstance() 默认就会切到 Xalan 解释型实现;且 xalan:xalan:2.7.3 的 pom 不传递依赖 serializer(实测 dependency:tree 仅显式声明的两个 jar),必须显式引入 serializer,否则运行时报 NoClassDefFoundError: org/apache/xml/serializer/SerializerTrace。
3.3 与 JDK 内置 XSLT 处理器的差异
JDK 自带一份 Xalan 内置 fork(包名 com.sun.org.apache.xalan.internal.*)。实测在无 xalan.jar 的 classpath 下运行 TransformerFactory.newInstance().getClass().getName():
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
两者关系与差异:
- 内置版随 JDK 发布,版本滞后且不可单独升级;Xalan-J 独立 jar 可自行选 2.7.2/2.7.3,随时升级;
- Xalan-J 提供命令行工具
org.apache.xalan.xslt.Process、XSLTC 独立编译器(把样式表预编译为字节码 translet)、解释型与编译型双引擎可切换; - 内置版仅保留 XSLTC 引擎,无命令行 Process、无 translet 预编译分发能力。
四、部署步骤
4.1 安装 OpenJDK 17 与 Maven
dnf install -y java-17-openjdk-devel maven
安装完成后先确认版本(注意:CentOS 8 的坑——默认 java 不会自动切到 17):
java -version
⚠️ 实测回显(安装后默认仍为 1.8):
openjdk version "1.8.0_312" OpenJDK Runtime Environment (build 1.8.0_312-b07)
用 alternatives 把默认 JVM 切到 17:
alternatives --set java java-17-openjdk.x86_64
alternatives --set javac java-17-openjdk.x86_64
java -version
javac -version
✅ 实测回显:
openjdk version "17.0.1" 2021-10-19 LTS OpenJDK Runtime Environment 21.9 (build 17.0.1+12-LTS) OpenJDK 64-Bit Server VM 21.9 (build 17.0.1+12-LTS, mixed mode, sharing) javac 17.0.1
4.2 配置 JAVA_HOME 与系统编码
写入 /etc/profile.d/java17.sh,登录与开机自动生效:
cat > /etc/profile.d/java17.sh <<'EOF'
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk
export LANG=C.UTF-8
EOF
source /etc/profile.d/java17.sh
mvn -version
✅ 实测回显:
Apache Maven 3.5.4 (Red Hat 3.5.4-5) Maven home: /usr/share/maven Java version: 17.0.1, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
💡
LANG=C.UTF-8必须设置:实测 POSIX 缺省 locale 下裸javac按 US-ASCII 读取 UTF-8 源码会报error: unmappable character (0xE9) for encoding US-ASCII,命令行传中文-PARAM也会乱码。详见第七节。
4.3 创建 Maven 工程
mkdir -p /opt/xalan-demo/src/main/java/com/example /opt/xalan-demo/data /opt/xalan-demo/out
cat > /opt/xalan-demo/pom.xml <<'EOF'
<?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>xalan-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>xalan</groupId>
<artifactId>xalan</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>xalan</groupId>
<artifactId>serializer</artifactId>
<version>2.7.3</version>
</dependency>
</dependencies>
</project>
EOF
💡
project.build.sourceEncoding=UTF-8保证 Maven 编译不受系统 locale 影响(实测同一份含中文注释的源码,Maven 构建成功、裸 javac 报 unmappable character 错误)。
4.4 引入依赖并构建
cd /opt/xalan-demo
mvn -B package
mvn -B dependency:tree | grep -E "xalan|serializer"
✅ 实测回显(dependency:tree 摘要):
[INFO] +- xalan:xalan:jar:2.7.3:compile [INFO] \- xalan:serializer:jar:2.7.3:compile [INFO] BUILD SUCCESS
构建产物在 target/classes,本地仓库 jar 位于 /root/.m2/repository/xalan/。
4.5 路径一:命令行 Process 直接变换
先准备样例数据(data/books.xml 与 data/books.xsl 全文见《Apache Xalan 快速入门与集成示例》同款样例,此处给最小可跑版本):
cd /opt/xalan-demo
export CP=/root/.m2/repository/xalan/xalan/2.7.3/xalan-2.7.3.jar:/root/.m2/repository/xalan/serializer/2.7.3/serializer-2.7.3.jar
java -cp $CP org.apache.xalan.xslt.Process -IN data/books.xml -XSL data/books.xsl -OUT out/cli.html
head -7 out/cli.html
⚠️ 实测注意:
-OUT指向的输出目录必须预先存在(上面 4.3 已mkdir out),否则报XSLT Error (java.io.FileNotFoundException): out/cli.html (No such file or directory),Process 不会自动创建目录。
✅ 实测回显(out/cli.html 头部):
<html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>图书清单</title> </head> <body> <h1>图书清单(关键字:编程)</h1>
参数化变换(-PARAM 传入关键字 Java):
java -cp $CP org.apache.xalan.xslt.Process -IN data/books.xml -XSL data/books.xsl -PARAM keyword Java -OUT out/cli-java.html
grep -o "共 [0-9]* 本匹配" out/cli-java.html
✅ 实测回显:
共 2 本匹配
4.6 路径二:Java Transformer API 变换
创建 src/main/java/com/example/XalanDemo.java:
package com.example;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
/**
* Xalan-J 快速入门:TransformerFactory + Transformer 完成 XSLT 变换。
* 用法: java com.example.XalanDemo <xml> <xsl> <out> [paramValue]
*/
public class XalanDemo {
public static void main(String[] args) throws Exception {
// 1. 打印当前 JAXP 实际加载的 TransformerFactory 实现类
TransformerFactory factory = TransformerFactory.newInstance();
System.out.println("TransformerFactory 实现类: " + factory.getClass().getName());
// 2. 准备输入源与输出目标
Source xmlSource = new StreamSource(new File(args[0]));
Source xslSource = new StreamSource(new File(args[1]));
Result result = new StreamResult(new File(args[2]));
// 3. 用样式表编译 Transformer,并设置输出属性与外部参数
Transformer transformer = factory.newTransformer(xslSource);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
if (args.length > 3) {
transformer.setParameter("keyword", args[3]);
}
// 4. 执行转换
transformer.transform(xmlSource, result);
System.out.println("转换完成, 输出文件: " + args[2]);
}
}
重新打包并运行(默认工厂 + 显式切换 XSLTC 两种方式各跑一次):
mvn -B -q package
java -cp "target/classes:$CP" com.example.XalanDemo data/books.xml data/books.xsl out/api.html
java -Djavax.xml.transform.TransformerFactory=org.apache.xalan.xsltc.trax.TransformerFactoryImpl \
-cp "target/classes:$CP" com.example.XalanDemo data/books.xml data/books.xsl out/api-xsltc.html
✅ 实测回显:
TransformerFactory 实现类: org.apache.xalan.processor.TransformerFactoryImpl 转换完成, 输出文件: out/api.html TransformerFactory 实现类: org.apache.xalan.xsltc.trax.TransformerFactoryImpl 转换完成, 输出文件: out/api-xsltc.html
💡 第一行为默认结果:classpath 有 xalan.jar 时,ServiceLoader 依 3.2 节的服务文件选中解释型实现;用
-D系统属性可无缝切到 XSLTC 编译型。OpenJDK 17 实测两种实现均可正常运行,无需--add-opens。
五、配置说明
5.1 环境变量与 JVM 堆
修改位置:
- 全局环境(JAVA_HOME、LANG):
/etc/profile.d/java17.sh(4.2 节已建); - Maven 构建进程堆:环境变量
MAVEN_OPTS,或写入/etc/profile.d/maven.sh/~/.mavenrc; - 命令行 java 进程堆:直接在
java命令后加-Xms/-Xmx(不读取 MAVEN_OPTS/JAVA_OPTS)。
XSLTC 编译大样式表、转换大文档时建议堆设为 256~512MB:
# Maven 构建进程
MAVEN_OPTS="-Xms256m -Xmx512m" mvn -B clean package
# 命令行变换进程
java -Xms256m -Xmx512m -cp $CP org.apache.xalan.xslt.Process -IN data/books.xml -XSL data/books.xsl -OUT out/cli.html
实测验证:把带堆参数的进程保持存活后抓取回显——
ps -ef | grep [j]ava
✅ 实测回显(CentOS 8,MAVEN_OPTS 生效的 Maven 构建进程):
java -Xms256m -Xmx512m -classpath /usr/share/java/plexus-classworlds.jar -Dclassworlds.conf=/usr/share/maven/bin/m2.conf -Dmaven.home=/usr/share/maven -Dlibrary.jansi.path=/usr/share/maven/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/opt/xalan-demo
jps -v
✅ 实测回显(命令行 java 进程):
1149 MemoryDemo -Xms256m -Xmx512m
程序内 Runtime.getRuntime().maxMemory() 实测打印 JVM 最大堆(maxMemory): 512 MB,与 -Xmx512m 一致。
5.2 JAXP 工厂选择
- 系统属性方式:
-Djavax.xml.transform.TransformerFactory=org.apache.xalan.xsltc.trax.TransformerFactoryImpl(XSLTC)或org.apache.xalan.processor.TransformerFactoryImpl(解释型),优先级最高; - 不设置时:classpath 有 xalan.jar → 解释型实现(ServiceLoader);无 xalan.jar → JDK 内置
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; - 也可写入
$JAVA_HOME/conf/jaxp.properties(JDK 9+)实现全局默认,无需每个命令加-D。
5.3 Xalan 自身无配置文件
Xalan-J 没有独立服务进程与配置文件,一切行为由 JAXP 系统属性、命令行参数与代码内 setAttribute/setOutputProperty 控制,无需开机自启服务(因此本文不涉及 systemd 单元管理)。
六、升级与多版本管理
- 升级:修改 pom 中两个依赖的
<version>(如 2.7.2 → 2.7.3)后重新mvn -B clean package即可;2.7.x 系列间 API 兼容,实测 2.7.3 在 OpenJDK 17 正常编译运行; - 多版本共存:为不同版本建独立 lib 目录,运行时用
-cp指定对应目录,例如/opt/xalan-2.7.2/lib/与/opt/xalan-2.7.3/lib/各放一对xalan-*.jar + serializer-*.jar; - 禁止同一 classpath 混放两个版本的 xalan.jar,实测会因
META-INF/services重复导致工厂选择不确定; - 升级后用
java -cp <对应xalan.jar> org.apache.xalan.Version核对版本号(实测输出Xalan Java 2.7.3)。
七、注意事项
- 系统编码三坑(实测):POSIX locale 下 (a) 裸
javac报unmappable character ... US-ASCII,解法javac -encoding UTF-8或设LANG=C.UTF-8;(b) 命令行-PARAM传中文值乱码并导致匹配失败,解法同上;(c) Java 程序System.out中文变问号,解法同上。 - 输出目录:Process 不自动创建
-OUT父目录,先mkdir -p out。 - serializer 缺失:只放 xalan.jar 运行报
NoClassDefFoundError: org/apache/xml/serializer/SerializerTrace;java -jar直跑时依赖 jar 必须按 MANIFESTClass-Path: xercesImpl.jar xml-apis.jar serializer.jar的精确文件名与 xalan.jar 同目录放置(放serializer-2.7.3.jar无效,实测须改名serializer.jar)。 - 错误退出路径二次异常:样式表 XML 不合法时,Process 先打印
[Fatal Error] ... SAXParseException(真正原因),随后可能再抛RuntimeException: ... "format" is null(错误收尾路径的二次异常),定位以第一条为准;-EDUMP生成的转储文件记录的是二次异常堆栈。 - JDK 17 兼容性:实测 Xalan-J 2.7.3 解释型与 XSLTC 引擎、命令行、Maven 构建均正常,无需
--add-opens;如使用 2.7.1 及更早版本遇到模块化访问限制,优先升级到 2.7.3。 - alternatives 默认 JVM:
dnf install java-17-openjdk-devel后默认 java 仍是 1.8(实测),必须alternatives --set切换,否则 Maven 用错 JDK。
八、常见问题
Q1: 运行时报 java.lang.NoClassDefFoundError: org/apache/xml/serializer/SerializerTrace 怎么办?
答:classpath 缺 serializer。Maven 工程确认显式声明 xalan:serializer:2.7.3(2.7.3 的 xalan pom 不传递该依赖);java -cp 命令确认两个 jar 都在;java -jar 方式确认 serializer.jar 与 xalan.jar 同目录。
Q2: TransformerFactory.newInstance() 拿到的是 com.sun.org.apache.xalan.internal...(JDK 内置),不是独立版 Xalan?
答:说明运行时 classpath 没有 xalan.jar,或被更高优先级的系统属性覆盖。检查 -cp;确认后仍想换实现,加 -Djavax.xml.transform.TransformerFactory=org.apache.xalan.processor.TransformerFactoryImpl(解释型)或 org.apache.xalan.xsltc.trax.TransformerFactoryImpl(XSLTC)。
Q3: 命令行 -PARAM 传中文后转换结果里是问号/乱码?
答:locale 问题。执行 export LANG=C.UTF-8 后重试(实测同一命令在 POSIX locale 下 -PARAM keyword 数据库 乱码、设 LANG 后输出 <h1>图书清单(关键字:数据库)</h1> 正常)。
Q4: javac 编译含中文注释的源码报 114 个 unmappable character 错误?
答:裸 javac 按 US-ASCII 读源码。改用 javac -encoding UTF-8 ...;走 Maven 构建则无此问题(pom 已配 sourceEncoding)。
Q5: Process 报 FileNotFoundException: out/cli.html (No such file or directory)?
答:输出目录不存在,Process 不会自动创建。先 mkdir -p out 再执行。
Q6: org.apache.xalan.xsltc.cmdline.Transform 报 Translet errors:Cannot find class 'data/books.xml'?
答:实测 2.7.3 中该命令实际生效的参数顺序是 XML 文档在前、translet 类名在后(与部分帮助文本相反)。正确用法:java -cp "out/translet:$CP" org.apache.xalan.xsltc.cmdline.Transform data/books.xml BooksTranslet。另注意它失败时退出码仍为 0,脚本判断要靠输出内容。
九、卸载方式
# 1. 删除示例工程与本地部署的 jar 目录
rm -rf /opt/xalan-demo
# 2. 清理 Maven 本地仓库中的 Xalan 组件(可选)
rm -rf /root/.m2/repository/xalan
# 3. 卸载 Maven 与 JDK(如服务器无其他用途;可单独卸载)
dnf remove -y maven
dnf remove -y java-17-openjdk-devel
# 4. 删除环境变量脚本
rm -f /etc/profile.d/java17.sh
💡 若服务器仍需运行其他 Java 应用,只执行第 1、2 步即可;Xalan-J 以 jar 形式随工程分发,删除工程目录与本地仓库缓存即完成卸载。
十、参考链接
- Apache Xalan 官网:https://xalan.apache.org/
- Xalan-J 文档主页:https://xalan.apache.org/xalan-j/
- Xalan-J 命令行工具文档:https://xalan.apache.org/xalan-j/commandline.html
- Maven Central:xalan:xalan:2.7.3:https://central.sonatype.com/artifact/xalan/xalan/2.7.3
- Maven Central 仓库目录:https://repo1.maven.org/maven2/xalan/xalan/
- GitHub 源码仓库(apache/xalan-java):https://github.com/apache/xalan-java