在Java中能执行命令的类其实并很少,不像php那样各类的命令执行函数。在Java中目前所知的能执行命令的类也就两种,分别是Runtime和 ProcessBuilder类。php
关于Runtime具体的使用能够看这篇文章,反射去调用Runtime。html
Java学习之反射篇java
@WebServlet("/execServlet") public class execServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String exec = request.getParameter("exec"); Process res = Runtime.getRuntime().exec(exec); InputStream inputStream = res.getInputStream(); ServletOutputStream outputStream = response.getOutputStream(); int len; byte[] bytes = new byte[1024]; while ((len = inputStream.read(bytes))!=-1){ outputStream.write(bytes,0,len); }
这里来运行一下,传入一个命令看看能不能正常运行。数组
http://localhost:8080/untitled9_war_exploded/execServlet?exec=ipconfig
咱们来看看他具体的实现,首先跟踪一下getRuntime()方法。函数
看到调用方法就不作任何的处理就直接返回了currentRuntime对象,上面能够看到currentRuntime是Runtime的实例对象。也就是说每次调用getRuntime()方法都会new一个Runtime的对象。学习
官方文档说明:ui
每一个Java应用程序都有一个Runtime类的Runtime ,容许应用程序与运行应用程序的环境进行接口。 当前运行时能够从getRuntime方法得到。 应用程序没法建立本身的此类的实例。
再来跟踪一下exec方法,看看exec方法的具体实现this
exec中有多个重载方法,在跟踪返回值的exec操作系统
仍是他的重载方法,再跟踪命令行
这里会发现不同了,返回了
return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start();
在跟踪的时候会发现,咱们传入的ipconfig会在cmdarray变量里面进行传入,其余值都是null。也就是说该类的底层其实仍是ProcessBuilder这个类来进行实现的。
前面的Process res = Runtime.getRuntime().exec(exec);
返回类型为 Process,是经过new ProcessBuilder并传入命令,再去调用start方法返回得来的。
但咱们后面的res.getInputStream();
,getInputStream();是怎么来的呢?Process 是一个抽象类,包含了6个抽象方法。
abstract public OutputStream getOutputStream(); abstract public InputStream getInputStream(); abstract public InputStream getErrorStream(); abstract public int waitFor() throws InterruptedException; abstract public int exitValue(); abstract public void destroy();
跟踪一下,这几个方法,调用的方法,除了start方法外,其余的几个方法都是进行设置值。这里就不一一演示了。
return new ProcessBuilder(cmdarray) .environment(envp) .directory(dir) .start();
跟踪start方法
能够看到start方法里面,调用了process的实现类。
package com.test; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; @WebServlet("/exec2Servlet") public class exec2Servlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String exec = request.getParameter("exec"); ServletOutputStream outputStream = response.getOutputStream(); ProcessBuilder processBuilder = new ProcessBuilder(exec); Process res = processBuilder.start(); InputStream inputStream = res.getInputStream(); int len ; byte[] bytes =new byte[1024]; while ((len = inputStream.read(bytes))!=-1){ outputStream.write(bytes,0,len); } } }
使用ProcessBuilder的方式也是能够执行命令的。
后面专门开一个标题,作一个前面知识的补充。
destroy() 杀掉子进程。 exitValue() 返回子进程的出口值。 InputStream getErrorStream() 得到子进程的错误流。 InputStream getInputStream() 得到子进程的输入流。 OutputStream getOutputStream() 得到子进程的输出流。 waitFor() 致使当前线程等待,若是必要,一直要等到由该 Process 对象表示的进程已经终止。
前面会发现的一个问题在这里作一个解疑,process既然是抽象类,不能new对象,获取到他的实例类的呢?
前面咱们调试代码的时候,发现了可使用ProcessBuilder的Start方法进行返回,第二种方法就是Runtime的exec方法,但Runtime的exec方法底层依然是ProcessBuilder的Start方法进行实现的。
这里还有必要说的一个问题,ProcessBuilder与Runtime.exec()的区别是什么?
在网上查阅了一些思路得出了如下的结论。
ProcessBuilder.start() 和 Runtime.exec() 方法都被用来建立一个操做系统进程(执行命令行操做),并返回 Process 子类的一个实例,该实例可用来控制进程状态并得到相关信息。 ProcessBuilder.start() 和 Runtime.exec()传递的参数有所不一样,Runtime.exec()可接受一个单独的字符串,这个字符串是经过空格来分隔可执行命令程序和参数的;也能够接受字符串数组参数。而ProcessBuilder的构造函数是一个字符串列表或者数组。列表中第一个参数是可执行命令程序,其余的是命令行执行是须要的参数。
https://honeypps.com/java/process-builder-quick-start/
在遇到一些问题的时候,多打debug也是个不错的选择,多打debug能比较清晰的分析到问题所在。例如调试代码的时候,能够打个debug一层层去分析也会发现一些有意思的东西。