什么是ProcessBuilder类?
- 此类用于创建操作系统进程。
- 每个ProcessBuilder实例管理一个进程的属性。
- start()方法使用这些属性创建一个新的Process实例。 可以从同一实例重复调用start()方法以创建具有相同或相关属性的新子进程。
每个进程构建器管理的进程属性:
- 命令,表示要调用的外部程序文件及其参数(如果有)。
哪些字符串列表表示有效的操作系统命令取决于系统。- 环境,依赖于系统的映射(从变量到值的)。初始值是当前进程环境的副本(可以System.getenv()获取操作系统的环境变量 )。
- 一个工作目录,默认值是当前进程的当前工作目录,通常是系统属性user.dir命名的目录。
- 标准输入的来源,默认情况下,子进程从管道读取输入。可以通过Process.getOutputStream()返回的输出流来访问这个管道。然而,标准的输入可被重定向到使用另一来源redirectInput。在这种情况下,Process.getOutputStream()将返回一个空输出流,导致:write方法总是抛出IOException;close方法什么都不会做。
- 标准输出和标准错误输出,默认情况下,子进程将标准输出和标准错误写入管道。 Java 代码可以通过Process.getInputStream()和Process.getErrorStream()返回的输入流来访问这些管道。 但是,可以使用redirectOutput和redirectError标准输出和标准错误重定向到其他目的地。 在这种情况下,Process.getInputStream()和Process.getErrorStream()将返回一个空输入流,为导致:read方法总是返回-1;available方法总是返回0;close方法什么都不会做。
- 一个redirectErrorStream属性,最初,此属性为false ,这意味着子流程的标准输出和错误输出被发送到两个单独的流,可以使用Process.getInputStream()和Process.getErrorStream()方法访问它们。
如果该值设置为true ,则:
标准错误与标准输出合并并始终发送到同一目的地(这使得将错误消息与相应的输出关联起来更容易);
标准错误和标准输出的共同目的地可以使用redirectOutput;
创建子进程时, redirectError忽略由redirectError方法设置的任何重定向;
从Process.getErrorStream()返回的流将始终是空输入流。- 修改进程构建器的属性将影响随后由该对象的start()方法启动的进程,但永远不会影响先前启动的进程或 Java 进程本身。
- 多线程不安全
ProcessBuilder类源码分析
public final class ProcessBuilder{
private List<String> command;
private File directory;
private Map<String,String> environment;
private boolean redirectErrorStream;
private Redirect[] redirects;
//使用指定的操作系统程序和参数构造进程构建器。
//此构造不会使副本command列表。 对列表的后续更新将反映在流程构建器的状态中。
//不检查command是否对应于有效的操作系统命令。
//形参:command – 包含程序及其参数的列表
public ProcessBuilder(List<String> command)
//使用指定的操作系统程序和参数构造进程构建器。
//这是一个方便的构造函数,它将流程构建器的命令设置为一个字符串列表,该列表包含与command数组相同的字符串,顺序相同。
//不检查command是否对应于有效的操作系统命令。
//形参:command – 包含程序及其参数的字符串数组
public ProcessBuilder(String... command) {
this.command = new ArrayList<>(command.length);
for (String arg : command)
this.command.add(arg);
}
//设置此进程构建器的操作系统程序和参数。
//这种方法不会使副本command列表。
//对列表的后续更新将反映在流程构建器的状态中。
//不检查command是否对应于有效的操作系统命令。
public ProcessBuilder command(String... command)
public ProcessBuilder command(List<String> command) {
if (command == null)
throw new NullPointerException();
this.command = command;
return this;
}
//返回此进程构建器的操作系统程序和参数。返回command集合
public List<String> command()
//返回进程构建器的环境,返回environment(ProcessEnvironment.environment())
public Map<String,String> environment()
//返回此流程构建器的工作目录。
//随后由该对象的start()方法start()子进程将使用它作为它们的工作目录。
//返回值可能为null ——这意味着使用当前 Java 进程的工作目录,通常是系统属性user.dir命名的目录,作为子进程的工作目录。
public File directory() {
return directory;
}
public ProcessBuilder directory(File directory) {
this.directory = directory;
return this;
}
//设置此进程构建器的标准输入源。 随后由该对象的start()方法start()子进程从该源获得它们的标准输入。
//如果源是Redirect.PIPE (初始值),则可以使用Process.getOutputStream()返回的输出流写入子进程的标准输入。 如果源设置为任何其他值,则Process.getOutputStream()将返回空输出流。
//形参:source – 新的标准输入源
public ProcessBuilder redirectInput(Redirect source)
//设置此进程构建器的标准输出目标。
//形参:目的地——新的标准输出目的地
public ProcessBuilder redirectOutput(Redirect destination)
//设置此流程构建器的标准错误目标。
//如果redirectErrorStream属性设置为true ,则此方法设置的重定向无效。
public ProcessBuilder redirectError(Redirect destination)
//将此进程构建器的标准输入源设置为文件。
public ProcessBuilder redirectInput(File file) {
return redirectInput(Redirect.from(file));
}
//将此进程构建器的标准输出目标设置为文件。
public ProcessBuilder redirectOutput(File file) {
return redirectOutput(Redirect.to(file));
}
//将此流程构建器的标准错误目标设置为文件。
public ProcessBuilder redirectError(File file) {
return redirectError(Redirect.to(file));
}
//返回此流程构建器的标准输入源。 随后由该对象的start()方法start()子进程从该源获得它们的标准输入。 初始值为Redirect.PIPE 。
public Redirect redirectInput()
//返回此流程构建器的标准输出目标。 随后由该对象的start()方法start()子进程将它们的标准输出重定向到该目的地。 初始值为Redirect.PIPE 。
public Redirect redirectOutput()
//返回此流程构建器的标准错误目标。 随后由该对象的start()方法start()子进程将它们的标准错误重定向到该目的地。 初始值为Redirect.PIPE 。
public Redirect redirectError()
//将子进程标准 I/O 的源和目标设置为与当前 Java 进程的源和目标相同。
//processBuilder.inheritIO()等价于processBuilder.redirectInput(Redirect.INHERIT).redirectOutput(Redirect.INHERIT).redirectError(Redirect.INHERIT)
public ProcessBuilder inheritIO()
//判断是否合并标准错误和标准输出。返回redirectErrorStream
public boolean redirectErrorStream()
public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) {
this.redirectErrorStream = redirectErrorStream;
return this;
}
//使用此进程构建器的属性启动一个新进程。
//新进程将调用command()给出的命令和参数,在directory()给出的工作目录中,以及environment()给出的进程环境。
public Process start() throws IOException {
// Must convert to array first -- a malicious user-supplied
// list might try to circumvent the security check.
String[] cmdarray = command.toArray(new String[command.size()]);
cmdarray = cmdarray.clone();
for (String arg : cmdarray)
if (arg == null)
throw new NullPointerException();
// Throws IndexOutOfBoundsException if command is empty
String prog = cmdarray[0];
SecurityManager security = System.getSecurityManager();
if (security != null)
security.checkExec(prog);
String dir = directory == null ? null : directory.toString();
for (int i = 1; i < cmdarray.length; i++) {
if (cmdarray[i].indexOf('\u0000') >= 0) {
throw new IOException("invalid null character in command");
}
}
try {
return ProcessImpl.start(cmdarray,
environment,
dir,
redirects,
redirectErrorStream);
} catch (IOException | IllegalArgumentException e) {
String exceptionInfo = ": " + e.getMessage();
Throwable cause = e;
if ((e instanceof IOException) && security != null) {
// Can not disclose the fail reason for read-protected files.
try {
security.checkRead(prog);
} catch (SecurityException se) {
exceptionInfo = "";
cause = se;
}
}
// It's much easier for us to create a high-quality error
// message than the low-level C code which found the problem.
throw new IOException(
"Cannot run program \"" + prog + "\""
+ (dir == null ? "" : " (in directory \"" + dir + "\")")
+ exceptionInfo,
cause);
}
}
}