`
lclcr
  • 浏览: 125456 次
  • 性别: Icon_minigender_1
  • 来自: 山东
社区版块
存档分类
最新评论

Java动态编译(二)

    博客分类:
  • JAVA
阅读更多
        在上一篇(Java动态编译(一))中我们提到了动态编译的三种方法,在这篇文章中讲解一些扩展的知识。
public abstract class Process extends Object

        ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获得相关信息。
        子进程没有自己的终端或控制台。它的所有标准io(即 stdin、stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。
        waitFor() 导致当前线程等待,如有必要,一直要等到由该Process对象表示的进程已经终止。但是如果我们在调用此方法时,如果不注意的话,很容易出现主线程阻塞,Process也挂起的情况。在调用waitFor() 的时候,Process需要向主线程汇报运行状况,所以要注意清空缓存区,即InputStream和ErrorStream,在网上,很多只提到处理InputStream,忽略了ErrorStream。
        举例1.
public class RuntimeExecDemo {
	public static void main(String[] args) throws InterruptedException {
		Runtime runtime = Runtime.getRuntime();   
		Process process = null;
		try {
			process = runtime.exec("cmd /c dir", null, new File("g:/"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		int	exitVal = process.waitFor();
		System.out.println("Process exitValue: " + exitVal);  
	}
}

        上面的程序并没有按照我们预想的那样列出g盘下的目录,而是挂起了,为什么会出现这种情况呢?java文档上说,创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准io(即 stdin、stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁
         当进程启动后,就会打开标准输出流和错误输出流准备输出,当进程结束时,就会关闭他们。举例1中,由于标准输出流有数据需要输出,但是我们却没有读取其中的数据,标准输出流就会一直等待数据被读取,程序挂起。
         举例2.处理标准输出流
public class RuntimeExecDemo {
	public static void main(String[] args) throws InterruptedException {
		Runtime runtime = Runtime.getRuntime();
		Process process = null;
		try {
			process = runtime.exec("cmd /c dir", null, new File("g:/"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
			String line = null;
			while ((line = br.readLine()) != null) {
				System.out.println(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		int exitVal = process.waitFor();
		System.out.println("Process exitValue: " + exitVal);
	}
}

        当我们处理了标准输出后,则程序可以正常运行了。但是,如果我们的命令是错误的,那么情况又会如何呢?即这里将dir修改成dira,然后执行。程序仍然可以正常运行,只不过结束的时候的exitVal不再是0。
        为什么会出现这种情况呢?这里就是当我们执行程序的时候,先打开的是标准输出流,然后等待我们从中读取数据,而我们对需要读取的数据做了读取处理。那么当接下来打开错误输出流的时候,尽管没有被读取,但是却不再阻塞,程序异常终止。
        但是,如果命令是正确的(即dira已经改回dir)再将process.getInputStream()修改成process.getErrorStream()呢?这次程序又一起挂起了。这是由于标准输出流是先打开并需要输出而,但是却并没有被读取;读取的是错误输出流,那么此时标准输出流的数据没有被读取导致程序不会结束。
        为了解决这个问题,可以根据输出的实际先后,先读取标准输出流,然后读取错误输出流。
        但是,很多时候不能很明确的知道输出的先后,特别是要操作标准输入的时候,情况就会更为复杂。这时候可以采用线程来对标准输出、错误输出和标准输入进行分别处理,根据他们之间在业务逻辑上的关系决定读取那个流或者写入数据。
         针对标准输出流和错误输出流所造成的问题,还可以使用ProcessBuilder的redirectErrorStream()方法将他们合二为一,这时候只要读取标准输出的数据就可以了。

         举例3.使用多线程
public class RuntimeExecDemo {
	public static void main(String[] args) {
		Runtime runtime = Runtime.getRuntime();   
		Process process = null;
		try {
			process = runtime.exec("cmd /c dir", null, new File("g:/"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		InputStream is = process.getInputStream();   
		InputStream es = process.getErrorStream();
		StringBuffer out = new StringBuffer(128);
		StreamGobbler isGobbler = new StreamGobbler(is, "gbk", out);
		StringBuffer err = new StringBuffer(128);
		StreamGobbler errorGobbler = new StreamGobbler(es, "gbk", err);
		isGobbler.start();
		errorGobbler.start();
		
		int exitVal = -1;
		try {
			exitVal = process.waitFor();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("out : \r\n" + out.toString());
		System.out.println("err : \r\n" + err.toString());
		System.out.println("Process exitValue: " + exitVal);  
	}
}

class StreamGobbler extends Thread {
    InputStream is;
    String encoding = "utf-8";
    StringBuffer result = new StringBuffer();

    /**
     *
     * @param is process.getErrorStream() or process.getInputStream()
     * @param encoding default is "utf-8", if the result include chinese, need to use "gbk"
     */
    StreamGobbler(InputStream is, String encoding, StringBuffer result) {
        this.is = is;
        this.encoding = encoding;
        this.result = result;
    }

    public void run() {
        try {
            InputStreamReader isr = new InputStreamReader(is, encoding);
            BufferedReader br = new BufferedReader(isr);
            String line=null;
            while ( (line = br.readLine()) != null) {
                result.append(line).append("\r\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

        举例4.是用ProcessBuilder.redirectErrorStream()
public boolean redirectErrorStream()

        通知进程生成器是否合并标准错误和标准输出。
        如果此属性为 true,则任何由通过此对象的 start() 方法启动的后续子进程生成的错误输出都将与标准输出合并,因此两者均可使用 Process.getInputStream() 方法读取。这使得关联错误消息和相应的输出变得更容易。初始值为 false。
public class ProcessBuilderDemo {
	public static void main(String[] args) {
		ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "dira");
		pb.directory(new File("g:\\"));
		pb.redirectErrorStream(true);
		Process process = null;
		try {
			process = pb.start();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
			String line = null;
			while ((line = br.readLine()) != null) {
				System.out.println(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		int exitVal = -1;
		try {
			exitVal = process.waitFor();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("Process exitValue: " + exitVal);
	}
}

<<OVER>>
分享到:
评论

相关推荐

    JavaCompiler --JDK6 API的简介(java动态编译)

    JavaCompiler --JDK6 API 的简介(java动态编译) JavaCompiler 是 Java 中的一个编译器接口,提供了编译 Java 源代码的功能。在 Java SE6 中,JavaCompiler 接口是 javax.tools 包的一部分,提供了标准的方式来...

    java动态编译指定java文件且将java文件生成jar

    在Java编程中,有时我们需要在运行时动态地编译源代码文件,并将其打包成可执行的JAR(Java Archive)文件。这样的需求通常出现在我们希望根据用户输入或特定条件生成并执行新的代码逻辑时。本教程将深入讲解如何...

    java动态编译java源文件

    Java动态编译指的是在程序运行时将Java源代码编译为字节码并加载到Java虚拟机(JVM)中的过程。这种技术在许多场景下非常有用,例如在开发环境中进行快速迭代、AOP(面向切面编程)或运行时代码生成等。Java的`javax...

    Java动态编译Java代码,运行在内存中,并执行

    添加动态执行的编译环境 options 是个集合,添加内容,字符集,classpath等 * 6.传入JavaFileObject的java文件,是个集合,创建JavaSourceObject实现这个接口,Kind.SOURCE.extension = '.java' * 7.创建任务并...

    java 动态编译.java文件,动态运行类

    在Java编程中,动态编译和运行类是一项重要的能力,特别是在需要实时更新代码或实现热部署的场景下。本文将深入探讨如何利用Java的API来动态编译.java源文件,并执行编译后的类。 首先,Java的`javac`命令行工具是...

    Java类动态加载(一)——java源文件动态编译为class文件

    这篇博客“Java类动态加载(一)——java源文件动态编译为class文件”可能主要探讨了如何在运行时将Java源代码(.java)编译成对应的字节码文件(.class),并将其加载到Java虚拟机(JVM)中。以下是对这个主题的详细解析...

    JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具

    JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA反编译工具JAVA...

    JDK8 下 SpringBoot 应用动态编译 Java 源码并注入 Spring 容器

    SpringBoot 应用动态编译 Java 源码并注入 Spring 容器,实现动态修改接口和抽象类的实现。注意,项目以 Jar 包形式启动时要在命令行引入 tools.jar 的包,IDEA下可直接调试。 基于接口、抽象类实现不停机动态调整...

    java 动态编译.java文件,动态运行类 _1.3

    在Java编程中,动态编译和运行类是一项重要的技术,特别是在需要实时更新代码或实现热部署的场景下。本文将详细讲解如何在Java中实现动态编译.java源代码文件并执行编译后的类。 首先,我们需要理解Java的编译过程...

    java 动态编译.java文件,动态运行类 _1.2

    1. **Java编译API:Javacompiler接口** Java提供了一个内置的`javax.tools.JavaCompiler`接口,它是Java工具接口(Java Tool API)的一部分,允许我们在程序中调用Javac编译器。要使用这个接口,首先需要引入`tools...

    Java 动态编译源码并调用

    Java 动态编译源码并调用是Java开发中的一种高级技巧,它允许程序在运行时根据需要编译和执行新的代码。这种能力在某些场景下非常有用,例如插件系统、元编程或者在运行时自动生成和执行特定逻辑。下面我们将详细...

    java反编译工具绿色版

    Java反编译工具是程序员在开发和调试过程中经常会用到的一种实用软件,它能够将已编译的Java字节码(.class文件)转换回源代码格式,这对于查看和理解第三方库或者研究已有的二进制代码非常有帮助。本绿色版的Java反...

    java内存动态编译执行

    Java内存动态编译执行是Java程序运行时的一个关键特性,它涉及到JVM(Java虚拟机)的即时编译器(JIT,Just-In-Time Compiler)和元空间(Metaspace)等核心概念。在Java的世界里,代码首先会被解释器逐行解释执行,...

    java动态编译 ,写了一个jsp在页面上编辑java代码,然后servlet动态编译并执行main方法

    Java动态编译是一种技术,允许程序在运行时将源代码转换为字节码并加载到JVM(Java虚拟机)中。这种能力使得Java应用程序能够根据需要编译和执行新的代码,增强了软件的灵活性和可扩展性。在给定的场景中,用户通过...

    java反编译工具

    Java反编译工具是开发者在特定情况下非常有用的工具,它能够将已经编译好的.class文件转换回源代码的.java文件。这种技术对于软件维护、学习他人代码、逆向工程或者理解已有的二进制库的工作原理都至关重要。本文将...

    利用Java的动态编译、动态加载结合EasyRules实现业务规则的动态性的项目源码

    利用Java的动态编译、动态加载结合EasyRules实现业务规则的动态性的项目源码,具体详情请查看相关博客https://blog.csdn.net/qq_31142553/article/details/85013989

    咖啡图形的java反编译工具(实用)

    Java反编译是将已编译的Java字节码(.class文件)转换回源代码(.java文件)的过程,这对于理解已加密或混淆的代码、逆向工程库或者研究软件内部工作原理非常有用。这款名为“咖啡图形的java反编译工具”很可能是一...

    JAVA反编译软件(可将class文件反编译为java文件)

    Java字节码是一种中间语言,由JVM(Java虚拟机)执行,而反编译就是尝试将这种二进制形式还原成人类可读的代码。 2. **Java反编译的用途**: - **代码理解**:当没有源代码时,反编译可以帮助理解第三方库的内部...

Global site tag (gtag.js) - Google Analytics