Java ProcessBuilder类源码分析(开发“Java命令执行器”前期准备)

什么是ProcessBuilder类?

  1. 此类用于创建操作系统进程。
  2. 每个ProcessBuilder实例管理一个进程的属性。 
  3. start()方法使用这些属性创建一个新的Process实例。 可以从同一实例重复调用start()方法以创建具有相同或相关属性的新子进程。

每个进程构建器管理的进程属性:

  1. 命令,表示要调用的外部程序文件及其参数(如果有)。
    哪些字符串列表表示有效的操作系统命令取决于系统。
  2. 环境,依赖于系统的映射(从变量到值的)。初始值是当前进程环境的副本(可以System.getenv()获取操作系统的环境变量 )。
  3. 一个工作目录,默认值是当前进程的当前工作目录,通常是系统属性user.dir命名的目录。
  4. 标准输入的来源,默认情况下,子进程从管道读取输入。可以通过Process.getOutputStream()返回的输出流来访问这个管道。然而,标准的输入可被重定向到使用另一来源redirectInput。在这种情况下,Process.getOutputStream()将返回一个空输出流,导致:write方法总是抛出IOException;close方法什么都不会做。
  5. 标准输出和标准错误输出,默认情况下,子进程将标准输出和标准错误写入管道。 Java 代码可以通过Process.getInputStream()和Process.getErrorStream()返回的输入流来访问这些管道。 但是,可以使用redirectOutput和redirectError标准输出和标准错误重定向到其他目的地。 在这种情况下,Process.getInputStream()和Process.getErrorStream()将返回一个空输入流,为导致:read方法总是返回-1;available方法总是返回0;close方法什么都不会做。
  6. 一个redirectErrorStream属性,最初,此属性为false ,这意味着子流程的标准输出和错误输出被发送到两个单独的流,可以使用Process.getInputStream()和Process.getErrorStream()方法访问它们。
    如果该值设置为true ,则:
    标准错误与标准输出合并并始终发送到同一目的地(这使得将错误消息与相应的输出关联起来更容易);
    标准错误和标准输出的共同目的地可以使用redirectOutput;
    创建子进程时, redirectError忽略由redirectError方法设置的任何重定向;
    从Process.getErrorStream()返回的流将始终是空输入流。
  7. 修改进程构建器的属性将影响随后由该对象的start()方法启动的进程,但永远不会影响先前启动的进程或 Java 进程本身。
  8. 多线程不安全

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);
        }
    }
}

おすすめ

転載: blog.csdn.net/qq_40100414/article/details/121353969