3

我听说您可以使用 Process 或使用 ClassLoaders 运行其他 java 文件。

我有一个可执行的 jar 'test.jar' 和一个名为Test.

我找到了一种使用 Process 运行的方法。我需要知道如何使用 ClassLoaders。

Process p = Runtime.getRuntime().exec("java -jar test.jar");
BufferedInputStream bis = new BufferedInputStream(p.getInputStream());
synchronized (p) {
   p.waitFor();
}
int b=0;
while((b=bis.read()) >0){
   System.out.print((char)b);    
}
4

4 回答 4

5

一些让你开始的代码,基本上就是@Felipe所说的。

请记住,这并不完全相同。你没有产生一个新进程,只是一个新线程;您可能会遇到一些巨大的类加载问题;System.exit()文件内部之类的东西Test.jar也会导致容器程序终止,等等。

File f = new File("Test.jar");
ClassLoader cl = new URLClassLoader(new URL[] { f.toURI().toURL() }, null);
final Class<?> claxx = cl.loadClass("my.package.Test");
final Method main = claxx.getMethod("main", String[].class);

Thread th = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            main.invoke(claxx, new Object[] { new String[] { "Param1", "Param2" } });
        } catch (Throwable th) {
            // TODO
        }
    }
});

th.setContextClassLoader(cl);
th.start();

th.join();
于 2012-08-31T11:59:12.030 回答
2

基本方法是:

  • 创建类加载器,
  • 阅读清单以找到入口点类
  • 加载入口点类,
  • 使用反射来获取它的public static void main(String[])方法,并且
  • main从新线程调用该方法,传递命令的命令行参数。

在这种特殊情况下,问题在于设置 System.in 和 System.out 流,以便您可以与在同一 JVM 中运行的“子应用程序”通信。问题是“子应用程序”期望 System.out 是它写入输出的流,并且“父应用程序”将从中读取。但是“父应用程序”期望 System.out 成为控制台(或其他)的输出流。这是行不通的,因为 System.in/out/err 是类字段,不可避免地对两个“应用程序”具有相同的值。

解决方案是将父应用程序类的代码更改为不使用 System.in/out/err 字段。在“父级”调用main子级上的方法之前,它必须使用System.setOut(...)将其设置为将写入缓冲区的自定义输出流实例。然后,您需要一个匹配的输入流实例,“父”可以从...中读取来代替bis您的代码。

您需要解决的另一件事是“子”应用程序调用System.exit(). 这将产生对父母拔掉插头的不幸影响。

还有各种各样的其他复杂性,比如清理线程和“孩子”创建的其他东西。其中一些基本上是无法解决的,除非您的 JVM 实现了 Isolates JSR(主流 JVM 都没有这样做,AFAIK)。

简而言之,在一般情况下这样做真的很难,而且有些事情你根本无法完全正确。除非您可以消除/避免这些问题,否则实现替代入口点和替代通信方式要简单得多。

于 2012-08-31T11:59:47.053 回答
1

您可以使用类java.net.URLClassLoader。您必须实例化 URLClassLoader,传递所需 JAR 的 URL(可以是文件 URL)。然后你可以使用这个类加载器来加载带有 main 方法的类。URLClasLoader.loadClass() 方法将返回代表已加载类的 Class 实例。这样,您可以通过反射实例化它并进一步调用它。您必须直接调用 main() 方法。

于 2012-08-31T11:52:39.427 回答
0

此代码可能会帮助您:

import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

public class Bootstrap {

public static void main(String[] args) throws Exception {

    /*
        Assume your application has a "home" directory
        with /classes and /lib beneath it, where you can put
        loose files and jars.

        Thus,

        /usr/local/src/APP
        /usr/local/src/APP/classes
        /usr/local/src/APP/lib
     */

    String HOME = "/usr/local/src/YOURAPP";
    String CLASSES = HOME + "/classes";
    String LIB = HOME + "/lib";

    // add the classes dir and each jar in lib to a List of URLs.
    List urls = new ArrayList();
    urls.add(new File(CLASSES).toURL());
    for (File f : new File(LIB).listFiles()) {
        urls.add(f.toURL());
    }

    // feed your URLs to a URLClassLoader!
    ClassLoader classloader =
            new URLClassLoader(
                    urls.toArray(new URL[0]),
                    ClassLoader.getSystemClassLoader().getParent());

    // relative to that classloader, find the main class
    // you want to bootstrap, which is the first cmd line arg
    Class mainClass = classloader.loadClass(args[0]);
    Method main = mainClass.getMethod("main",
            new Class[]{args.getClass()});

    // well-behaved Java packages work relative to the
    // context classloader.  Others don't (like commons-logging)
    Thread.currentThread().setContextClassLoader(classloader);

    // you want to prune the first arg because its your main class.
    // you want to pass the remaining args as the "real" args to your main
    String[] nextArgs = new String[args.length - 1];
    System.arraycopy(args, 1, nextArgs, 0, nextArgs.length);
    main.invoke(null, new Object[] { nextArgs });
}

}

这里得到。

于 2012-08-31T12:09:14.440 回答