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です。

もう 1 つ対処する必要があるのは、「子」アプリケーションが を呼び出す可能性ですSystem.exit()。これは、親のプラグを抜くという不幸な結果をもたらします。

また、「子」によって作成されたスレッドやその他のもののクリーンアップなど、他のあらゆる種類の複雑さがあります。JVMがIsolates JSRを実装しない限り、これらのいくつかは基本的に解決できません(主流のJVMはそうではありません、AFAIK)。

要するに、一般的なケースでこれを行うのは非常に難しく、完全に正しくできないことがいくつかあります。これらの問題を排除/回避できない限り、代替のエントリポイントと代替の通信方法を実装する方がはるかに簡単です。

于 2012-08-31T11:59:47.053 に答える
1

クラスjava.net.URLClassLoaderを使用できます。目的の JAR の URL (ファイル URL の場合もある) を渡す URLClassLoader をインスタンス化する必要があります。次に、このクラス ローダーを使用して、メイン メソッドでクラスをロードできます。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 に答える