From 58f6b49f222d5aaf78f0016d59f2f994f090e74e Mon Sep 17 00:00:00 2001 From: liumangmang Date: Mon, 11 May 2026 14:04:01 +0800 Subject: [PATCH] docs(work): add PR7950 package startup flow summary --- .../pr7950-java-package-startup-flow.md | 321 ++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 src/work/project-summary/pr7950-java-package-startup-flow.md diff --git a/src/work/project-summary/pr7950-java-package-startup-flow.md b/src/work/project-summary/pr7950-java-package-startup-flow.md new file mode 100644 index 0000000..bfe4b59 --- /dev/null +++ b/src/work/project-summary/pr7950-java-package-startup-flow.md @@ -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` 解密运行。 + + + +## 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 的 `` 中声明的 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 + + com.github.core-lib + xjar + 4.0.1 + +``` + +### 默认加密参数 + +| 项目 | 值 | +| :--- | :--- | +| 密码 | `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` 启动脚本。