反射调用命令执行

Preface

开发者设计本地命令执行的本意是实现某些程序功能

Runtime

最简单的命令执行方法

Runtime.getRuntime().exec("calc");

跟踪调用栈可以发现,Runtime#exec并不是命令执行的最终点

java.lang.Runtime#exec

​ -> java.lang.ProcessBuilder#start

​ -> java.lang.ProcessImpl#start

​ -> ProcessImpl#

​ -> native create

ProcessBuilder

public final class ProcessBuilder {
    public ProcessBuilder(List<String> command) {
            if (command == null)
                throw new NullPointerException();
            this.command = command;
        }

    public ProcessBuilder(String... command) {
        this.command = new ArrayList<>(command.length);
        for (String arg : command)
            this.command.add(arg);
    }
    public Process start() throws IOException {
        // ...
        try {
            return ProcessImpl.start(cmdarray,
                                     environment,
                                     dir,
                                     redirects,
                                     redirectErrorStream);
        } // ...
    }
}
// ProcessBuilder processBuilder = new ProcessBuilder(Arrays.asList("calc"));
ProcessBuilder processBuilder = new ProcessBuilder("calc");
processBuilder.start();

ProcessImpl

非public类,需要使用反射调用

Class clazz = Class.forName("java.lang.ProcessImpl");
Method start = clazz.getDeclaredMethod("start", String[].class, Map.class, String.class, ProcessBuilder.Redirect[].class, boolean.class);
start.setAccessible(true);
start.invoke(clazz, new String[]{"calc"}, null, null, null, false);

ProcessImpl#start最终调用的是ProcessImpl的构造方法

Class clazz = Class.forName("java.lang.ProcessImpl");
Constructor constructor = clazz.getDeclaredConstructor(String[].class, String.class, String.class, long[].class, boolean.class);
constructor.setAccessible(true);
constructor.newInstance(new String[]{"calc"}, null, null, new long[]{-1,-1,-1}, false);

最后调用的是ProcessImpl#create native方法

Linux和Mac系统上则是交给UNIXProcess#forkAndExec native方法

Last updated