# process-demo Maven 单模块示例工程(目标 Java 17),用于学习 **`ProcessBuilder` / `Process` / `ProcessHandle`**:如何构建一个子进程、读写它的标准输入输出、重定向流、异步处理 I/O、获取执行状态码,以及组合成管道式调用和进程树管理。 所有说明文档与代码注释均为中文,配套教程见 [`process.md`](process.md)(大纲)、[`doc.md`](doc.md)(完整教程)与 [`blog.md`](blog.md) / [`blog2.md`](blog2.md)(博客文章)。 ## 技术栈 - Java 17(`maven.compiler.source/target = 17`,UTF-8 编码) - 运行时依赖:`logback-classic`(提供 SLF4J 日志) - 测试依赖:`junit-jupiter`(JUnit 5,由 `maven-surefire-plugin` 驱动) - 无 lint、无 `exec-maven-plugin` ## 目录结构 ``` src/main/java/space/anyi/process/ 示例代码(public static void main 入口) Jdk.java 共享工具:javac/java 绝对路径、编译 fixture QuickStart.java 示例一:构建并运行子进程 ProcessIOExample.java 示例二:标准输入/输出/错误流 ProcessRedirectExample.java 重定向:合并流、文件重定向、Redirect 枚举 ProcessAsyncExample.java 异步:线程 / CompletableFuture / onExit ExecuteStatusExample.java 示例三:waitFor 阻塞与超时销毁 ProcessBuilderExample.java 环境变量(environment()) ProcessPipelineExample.java 示例五:管道式组合调用 ProcessHandleExample.java 示例六:ProcessHandle 进程管理 src/main/resources/code/ 子进程程序(fixtures),各目录含 Main.java hello/ 打印问候语与 PID io/ 回显 stdin、统计并写入 stderr executeStatus/ 睡眠 10 秒后退出 processHandle/ 打印 PID/参数,睡眠 30 秒等待父进程观察 src/test/java/space/anyi/process/ ProcessMechanicsTest(端到端测试) doc.md / process.md / blog.md / blog2.md 教程与博客文档 ``` ## 运行示例 工程没有运行目标,编译后手动运行。需要把依赖 jar 一起放到 classpath(示例使用 slf4j 日志),否则出现 `NoClassDefFoundError: org/slf4j/LoggerFactory`。 ```bash mvn -q compile java -cp "target/classes:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout | tail -1)" space.anyi.process. ``` 例如: ```bash java -cp "target/classes:$(mvn -q dependency:build-classpath -Dmdep.outputFile=/dev/stdout | tail -1)" space.anyi.process.QuickStart ``` > 注意:示例的 `workDir` 相对于 `user.dir`,**必须从仓库根目录运行**,否则 `ProcessBuilder.start()` 抛 `IOException: error=2`。 ## 测试 ```bash mvn test ``` `ProcessMechanicsTest` 端到端驱动同一批 child fixtures(会先重新编译 `io/` 与 `executeStatus/`),并刻意覆盖「流排水 + 超时/`destroy`」的常见陷阱。同样要求从仓库根目录运行。 ## 教程要点(出自 doc.md 的踩坑总结) 1. `new BufferedReader(...).lines().forEach(...)` 会**阻塞当前线程直到子进程退出**;做限时 `waitFor(timeout)` 时必须在后台线程排水。 2. `Process.waitFor(timeout, unit)` 返回的是 `boolean` 而非退出码;超时返回 `false` 时子进程仍存活,此时调用 `exitValue()` 会抛 `IllegalThreadStateException`,须先 `destroy()` 再阻塞 `waitFor()`。 3. 生成的子进程 PATH 往往缺少 JDK bin 目录(`error=2`),请统一走 `Jdk.javac()` / `Jdk.java()` 的绝对路径,不要裸用 `javac` / `java`。 ## 约定 - 每个案例点由一个独立的 `static` 方法演示,统一由 `main()` 调用。 - 所有子进程程序均来自 `src/main/resources/code/`,不使用临时脚本或系统命令。 - 代码注释为中文,说明每步的作用与原因。