docs(work): add PR7950 package startup flow summary

This commit is contained in:
liumangmang
2026-05-11 14:04:01 +08:00
parent 514f3c78dc
commit 58f6b49f22
@@ -0,0 +1,321 @@
---
title: PR7950 Java 打包启动流程说明
icon: fa6-solid:box-archive
date: 2026-05-11
category:
- 工作
tag:
- PR7950
- Java
- 打包
- XJar
- 启动流程
---
# PR7950 Java 打包、压缩、XJar 加密与启动流程说明
整个流程从 `build.sh` 开始,先构建 Java 模块,再按清单提取业务 fat jar,随后用压缩工具拆分公共依赖,再使用 XJar 加密,最后把产物同步到 `binary/java` 运行目录。启动时不是直接执行 `java -jar`,而是通过 `xjar java ... -jar 服务.jar` 解密运行。
<!-- more -->
## 1. 总体流程
整个流程从 `build.sh` 开始,先构建 Java 模块,再按清单提取业务 fat jar,随后用压缩工具拆分公共依赖,再使用 XJar 加密,最后把产物同步到 `binary/java` 运行目录。启动时不是直接执行 `java -jar`,而是通过 `xjar java ... -jar 服务.jar` 解密运行。
### 流程步骤
1. **build.sh**: 准备 JDK/Maven,同步 encrypt XML
2. **Maven Build**: 构建 platform 与 platapp
3. **x64_raw**: 按 jar_list.txt 提取原始 fat jar
4. **compress**: 拆分 BOOT-INF/lib 依赖
5. **lib/common**: 输出公共 jar 与 requirements.txt
6. **XJar**: 加密压缩后的服务 jar
7. **binary**: 同步 jar 与公共依赖到运行目录
8. **startup**: xjar java 启动,动态加载公共依赖
> **一句话概括:**
> 打包阶段把业务 jar 变小并加密,启动阶段用 XJar 解密运行,再由 `sunri-spring-classload` 把拆出去的公共依赖加载回来。
## 2. 构建与提取 Jar
入口脚本位于:
`/home/liumangmang/IdeaProjects/PR7950/V1.00_2024/src_java/package/build.sh`
### 执行入口
```bash
cd /home/liumangmang/IdeaProjects/PR7950/V1.00_2024/src_java/package
bash build.sh
```
### 环境准备
脚本根据机器架构选择内置 JDK,并使用项目自带 Maven。
| 项目 | 路径/规则 |
| :--- | :--- |
| x86_64 JDK | `src_java/package/jdk-x64` |
| ARM JDK | `src_java/package/jdk-arm` |
| Maven | `src_java/package/maven` |
| 原始 jar 目标目录 | `src_java/package/java/jar/x64_raw` |
### Maven 构建顺序
```bash
cd src_java/platform
mvn clean install -DskipTests -T 1C
cd src_java/platapp
mvn clean package -DskipTests -T 1C
```
### 按 jar_list.txt 提取业务 Jar
`build.sh` 读取 `src_java/package/jar_list.txt`,在 `platform``platapp``target` 目录中查找同名 jar,排除 `original-*`,复制到 `x64_raw`
```text
src_java/package/jar_list.txt
src_java/platform/**/target/*.jar
src_java/platapp/**/target/*.jar
src_java/package/java/jar/x64_raw/*.jar
```
## 3. 压缩与依赖拆分
压缩工具源码位于:
`/home/liumangmang/IdeaProjects/PR7950/V1.00_2024/src_java/tool/sunri-package-compress-xjar/`
构建产物是带依赖的可执行 jar
`target/sunri-package-compress-xjar-1.0-jar-with-dependencies.jar`
### 配置文件
脚本从模板生成实际配置:
`src_java/package/java/tools/model_compress-xjar.config` -> `src_java/package/java/tools/compress-xjar.config`
| 配置项 | 作用 |
| :--- | :--- |
| `entryptXmlDir` | 读取每个服务的加密/保留依赖 XML,实际来自 `src_java/package/java/bin/encrypt/` |
| `rawJarDir` | 原始 fat jar 输入目录:`src_java/package/java/jar/x64_raw` |
| `compressJarDir` | 压缩后 jar 输出目录:`src_java/package/java/jar/x64_compress` |
| `libDir` | 被拆出去的公共依赖输出目录:`src_java/package/java/lib/common` |
| `xjarDir` | XJar 加密后 jar 输出目录:`src_java/package/java/jar/x64` |
| `enableCompress` | `1` 启用压缩,`0` 不压缩 |
### 依赖拆分规则
压缩逻辑在 `JarUtil.compress` 中实现,扫描 Spring Boot fat jar 的 `BOOT-INF/lib/*.jar`。符合以下条件的依赖会保留在业务 jar 内,其余依赖会被提取到 `lib/common`
* `cygbusiness-*` 开头的 jar 保留。
* `sunri-*` 开头的 jar 保留。
* 当前服务 XML 的 `<include>` 中声明的 jar 保留。
* 其他 jar 从业务 jar 删除,并复制到 `lib/common`
每个服务还会生成一个依赖清单:
`src_java/package/java/lib/common/服务名-requirements.txt`
该清单记录当前服务启动时需要从公共目录加载哪些被拆出去的 jar。
## 4. XJar 加密
压缩后,工具使用 XJar 对服务 jar 加密。相关代码位于:
`src_java/tool/sunri-package-compress-xjar/src/main/java/com/sunri/Main.java`
使用的依赖是:
```xml
<dependency>
<groupId>com.github.core-lib</groupId>
<artifactId>xjar</artifactId>
<version>4.0.1</version>
</dependency>
```
### 默认加密参数
| 项目 | 值 |
| :--- | :--- |
| 密码 | `xtptweb` |
| include | `/com/sunri/**`, `*.yaml`, `mapper/**.xml`, `*.yml` |
| exclude | 空 |
工具先输出 `服务名.xjar`,成功后再重命名为 `服务名.jar`。因此发布目录中看到的是 `.jar` 文件,但内容已经经过 XJar 处理。
```text
x64_compress/服务名.jar
↓ XJar encryption
x64/服务名.xjar
↓ rename
x64/服务名.jar
```
> **注意:**
> `src_java/platform/01_componnet/08_sunri-package-xjar/` 是历史/图形化加密工具组件;当前 `build.sh` 实际调用的是 `src_java/tool/sunri-package-compress-xjar/`。
## 5. 同步到 binary 运行目录
加密后的服务 jar 从源码打包目录复制到运行目录:
```text
src_java/package/java/jar/x64/*.jar
binary/java/jar/x64/*.jar
```
公共依赖和 `*-requirements.txt` 从源码打包目录复制到运行目录:
```text
src_java/package/java/lib/common/
binary/java/lib/common/
```
最终运行目录关键结构如下:
```text
binary/java/bin/
binary/java/bin/encrypt/
binary/java/jar/x64/
binary/java/jar/x64/xjar
binary/java/lib/common/
```
> **关键前提:**
> `binary/java/jar/x64/xjar` 是 Linux x86_64 可执行文件,启动加密 jar 时必须存在并具备执行权限。当前 `build.sh` 不负责编译这个可执行文件,只依赖运行目录已预置。
## 6. 启动流程
业务启动脚本位于:
`/home/liumangmang/IdeaProjects/PR7950/V1.00_2024/binary/java/bin/`
启动前需要设置 `PRJHOME` 指向项目运行根目录:
```bash
export PRJHOME=/home/liumangmang/IdeaProjects/PR7950/V1.00_2024
```
### 启动、停止、重启示例
```bash
bash ${PRJHOME}/binary/java/bin/cygsystemweb.sh start
bash ${PRJHOME}/binary/java/bin/cygsystemweb.sh stop
bash ${PRJHOME}/binary/java/bin/cygsystemweb.sh restart
```
每个业务脚本都会设置服务名、jar 路径、公共依赖路径和 JVM 参数,然后调用统一控制脚本:
```bash
JAVA_PROC_ROOT=${PRJHOME}/binary/java
JAVA_PROC_NAME="cygsystemweb"
JAVA_PROC_JAR_PATH=${JAVA_PROC_ROOT}/jar/x64/${JAVA_PROC_NAME}.jar
JAVA_PROC_LIB_PATH=${JAVA_PROC_ROOT}/lib/common
JVM_OPTS="-Xms512M -Xmx512M"
/bin/bash ${JAVA_PROC_ROOT}/bin/javacontrol.sh \
${JAVA_PROC_JAR_PATH} start ${JAVA_PROC_LIB_PATH} "${JVM_OPTS}"
```
### javacontrol.sh 最终命令
真正启动时不是直接 `java -jar`,而是通过 `xjar` 包装 `java` 命令:
```bash
nohup ${PRJHOME}/binary/java/jar/x64/xjar java \
-Dplainload.dir.path=${PRJHOME}/binary/java/lib/common \
-Djava.io.tmpdir=/home/sunri/patrolTemp \
-Dplainload.include.file=cygsystemweb-requirements.txt \
-DenableSkipAuth=false \
-Djava.net.preferIPv4Stack=true \
-Dfile.encoding=utf-8 \
-Duser.timezone=Asia/Shanghai \
-Drds.dynamic.sql=true \
-Xms512M -Xmx512M \
-XX:+UseG1GC \
-XX:ParallelGCThreads=8 \
-XX:NativeMemoryTracking=summary \
-jar ${PRJHOME}/binary/java/jar/x64/cygsystemweb.jar \
> /dev/null 2>&1 &
```
## 7. 动态加载公共依赖
公共依赖动态加载组件位于:
`/home/liumangmang/IdeaProjects/PR7950/V1.00_2024/src_java/platform/01_componnet/06_sunri-spring-classload/`
该组件通过 `META-INF/spring.factories` 注册 Spring Boot 早期监听器:
```text
org.springframework.context.ApplicationListener=\
com.sunri.PlainTextClassLoader
```
`PlainTextClassLoader` 启动时读取两个 JVM 参数:
| 参数 | 作用 |
| :--- | :--- |
| `-Dplainload.dir.path` | 公共依赖目录,例如 `binary/java/lib/common` |
| `-Dplainload.include.file` | 当前服务的依赖清单,例如 `cygsystemweb-requirements.txt` |
加载过程是:读取 `binary/java/lib/common/服务名-requirements.txt`,再把清单中的 jar 通过反射调用 `URLClassLoader.addURL` 加入当前 classloader。
```text
binary/java/lib/common/
├── cygsystemweb-requirements.txt
├── jackson-databind-*.jar
├── mysql-connector-*.jar
└── ...
PlainTextClassLoader
↓ read requirements
↓ addURL(jar)
Spring Boot application continues startup
```
> **运行时 classpath 组成:**
> 加密服务 jar 内保留的依赖,加上 `lib/common` 中按 `requirements.txt` 动态加载的依赖。
## 8. 新增服务维护清单
如果后续新增一个 Java 服务,需要同时维护以下内容,保证打包和启动链路完整。
*`src_java/package/jar_list.txt` 增加 `新服务.jar`
* 确认 Maven 构建后能在 `platform``platapp``target` 下生成同名 jar。
*`binary/java/bin/encrypt/` 增加 `新服务.xml`,并确保根节点 `service="新服务"`
* 在 XML 中声明压缩后必须保留在业务 jar 内的依赖,尤其是 Spring Boot 核心依赖、启动早期依赖、业务 SPI 和不能拆出的内部包。
*`binary/java/bin/` 增加 `新服务.sh`,设置 `JAVA_PROC_NAME``JVM_OPTS` 等参数。
* 执行 `build.sh` 后确认生成 `binary/java/jar/x64/新服务.jar`
* 确认生成 `binary/java/lib/common/新服务-requirements.txt`
* 启动前确认 `PRJHOME``xjar` 执行权限、公共依赖目录都正确。
## 9. 快速参考
### 打包命令
```bash
cd /home/liumangmang/IdeaProjects/PR7950/V1.00_2024/src_java/package
bash build.sh
```
### 启动命令
```bash
export PRJHOME=/home/liumangmang/IdeaProjects/PR7950/V1.00_2024
bash ${PRJHOME}/binary/java/bin/cygsystemweb.sh start
```
### 加密后 Jar
```text
binary/java/jar/x64/*.jar
```
### 公共依赖
```text
binary/java/lib/common/
binary/java/lib/common/*-requirements.txt
```
---
本文档根据当前仓库脚本和源码整理,重点覆盖 `build.sh``sunri-package-compress-xjar``sunri-spring-classload`、XJar 运行入口与 `binary/java/bin` 启动脚本。