Java与Python交互技术
一、技术选型
在JAVA中要调用Python主要有2种方案:
- 使用ProcessBuilder
- 直接使用new ProcessBuilder()
- 使用Runtime.getRuntime().exec()
- 使用Jython解释器
二、ProcessBuilder执行python
使用ProcessBuilder执行python最有代表性的是Runtime.getRuntime().exec(),下面是Runtime.getRuntime().exec()执行流程,下文主要对它进行展开。
2.1 Runtime.getRuntime()创建方式
Runtime.getRuntime()是单例模式:
public class Runtime {
private static final Runtime currentRuntime = new Runtime();
private static Version version;
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class {@code Runtime} are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the {@code Runtime} object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
……
}
2.2 Runtime.getRuntime().exec()的执行过程
Runtime.getRuntime().exec()本质是上是使用ProcessBuilder创建一个进程。
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
在start里面,执行了ProcessImpl.start()方法。
Process process = ProcessImpl.start(cmdarray, environment, dir, redirects, redirectErrorStream);
ProcessStartEvent event = new ProcessStartEvent();
if (event.isEnabled()) {
event.directory = dir;
event.command = String.join(" ", cmdarray);
event.pid = process.pid();
event.commit();
}
在ProcessImpl.start()中ProcessBuilder.Redirect表示子进程输入的源或子进程输出的目标。 每个Redirect
实例都是以下之一:
-
特殊值
Rediret.PIPE
-
重定向从文件读取,由调用
Redirect.from(File)
创建 -
重定向到写入文件,由调用
Redirect.to(File)
创建 -
重定向附加到文件,由调用
Redirect.appendTo(File)
创建在进程实例化时直接new ProcessImpl(),并将输入输出流代入。
Process p = new ProcessImpl
(toCString(cmdarray[0]),
argBlock, args.length,
envBlock, envc[0],
toCString(dir),
std_fds,
forceNullOutputStream,
redirectErrorStream);
然后通过native方法forkAndExec调用底层代码,并获得相应的pid。
pid = forkAndExec(launchMechanism.ordinal() + 1,
helperpath,
prog,
argBlock, argc,
envBlock, envc,
dir,
fds,
redirectErrorStream);
processHandle = ProcessHandleImpl.getInternal(pid);
在forkAndExec源码中,使用prog接收我们的执行命令,复制流进行数据传输。
JNIEXPORT jint JNICALL
Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,
jobject process,
jint mode,
jbyteArray helperpath,
jbyteArray prog,
jbyteArray argBlock, jint argc,
jbyteArray envBlock, jint envc,
jbyt