23

実行時に、アプリケーションが開始されたクラス名 (main() メソッドを含むクラス名) を特定したいのですが、別のスレッドにいて、スタック トレースが元のクラスにまでさかのぼりません。

システム プロパティと、ClassLoader が提供するすべてのものを検索しましたが、何も見つかりませんでした。この情報は入手できないだけですか?

ありがとう。

4

10 に答える 10

10

Tom Hawtin によるリンクのコメントを参照してください。最近の解決策は次のとおりです(Oracle JVMのみ):

public static String getMainClassAndArgs() {
    return System.getProperty("sun.java.command"); // like "org.x.y.Main arg1 arg2"
}

Oracle Java 7 でのみテスト済み。特殊なケースに関する詳細情報: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4827318

于 2014-11-07T16:27:21.677 に答える
4

私はそれを考え出した。この環境変数がオペレーティングシステム全体の他のJava実装に常に存在するかどうか誰かに教えてもらえますか?これは、Oracle JVMで、「org.xyClassName」のような文字列を生成します。

public static String getMainClassName() {
  for (final Map.Entry<String, String> entry : System.getenv().entrySet())
    if (entry.getKey().startsWith("JAVA_MAIN_CLASS")) // like JAVA_MAIN_CLASS_13328
      return entry.getValue();
  throw new IllegalStateException("Cannot determine main class.");
}
于 2009-06-02T14:58:08.963 に答える
4

JAVA_MAIN_CLASS 環境値は、プラットフォームによっては常に存在するとは限りません。Java プロセスを開始した「メイン」クラスの名前だけを取得したい場合は、次のようにします。

  public static String getMainClassName()
  {
    StackTraceElement trace[] = Thread.currentThread().getStackTrace();
    if (trace.length > 0) {
      return trace[trace.length - 1].getClassName();
    }
    return "Unknown";
  } 
于 2016-04-30T00:52:17.667 に答える
3

Thread.getAllStackTraces()を使用してみてください。現在のスレッドだけでなく、実行中のすべてのスレッドからのスタック トレースの Map を返します。

于 2009-06-02T14:48:22.820 に答える
2

明確にするために、「上からのパラメーター化」イディオムを使用することをお勧めします。あなたは最初に情報を持っています、それを保持してください。

テストランナーで使用するために、このようなもののためにRFE 4827318(6年前!)を入れました。

于 2009-06-02T15:00:29.740 に答える
1

main() メソッドを含むスレッドが終了し、Oracle JVM を使用していない場合でも、オペレーティング システムから情報を取得しようとすることができます。以下のコードは、Linux で JVM を起動するために使用されるコマンド ラインを取得しますが、Windows など用のバージョンを作成することもできます。次に、JVM への引数を調べて、アプリケーションのエントリ ポイントを見つけることができます。コマンドラインで直接指定するか、指定した jar のマニフェストで Main-Class: classname を探す必要があります。私は最初に System.getProperty("sun.java.command") を使用し、友人は必要に応じてこのメカニズムにフォールバックするだけです。

public final static long getSelfPid() {
    // Java 9 only
    // return ProcessHandle.current().getPid();
    try {
        return Long.parseLong(new File("/proc/self").getCanonicalFile().getName());
    } catch( Exception e ) {
        return -1;
    }
}

public final static String getJVMCommandLine() {
    try {
        // Java 9 only
        // long pid = ProcessHandle.current().getPid();
        long pid = getSelfPid();
        byte[] encoded = Files.readAllBytes(Paths.get("/proc/"+pid+"/cmdline"));
        // assume ISO_8859_1, but could look in /proc/<pid>/environ for LANG or something I suppose
        String commandLine = new String( encoded, StandardCharsets.ISO_8859_1 ); 
        String modifiedCommandLine = commandLine.replace((char)0, ' ').trim();
        return modifiedCommandLine;
    } catch( Exception e ) {
        return null;
    }
}`
于 2017-01-12T03:17:22.707 に答える
0

この情報をシステム プロパティに入れることをお勧めします。これは通常、スクリプトからアプリケーションを起動する場合に簡単に実行できます。

それができない場合は、すべてのアプリケーションの main() メソッドでプロパティを設定することをお勧めします。ここでの最も簡単な方法は、すべてのアプリが共通の基本クラスから「メイン クラス」を派生させ、そこで init ステップを実行することです。コマンドライン処理のためにこれを行うことがよくあります。

public class Demo extends Main {
    main(String[] args) {
        Main._main(new Demo (), args);
    }

    // This gets called by Main._main()
    public void run (String[] args) {
    }
}
于 2009-06-02T15:40:53.583 に答える
0

次のようなものはどうですか:

Map<Thread,StackTraceElement[]> stackTraceMap = Thread.getAllStackTraces();
for (Thread t : stackTraceMap.keySet())
{
    if ("main".equals(t.getName()))
    {
        StackTraceElement[] mainStackTrace = stackTraceMap.get(t);
        for (StackTraceElement element : mainStackTrace)
        {
            System.out.println(element);
        }
    }
}

これはあなたに次のようなものを与えるでしょう

java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:231)
java.lang.Thread.join(Thread.java:680)
com.mypackage.Runner.main(Runner.java:10)

ただし、メインスレッドはおそらく呼び出されることが保証され"main"ていません-含まれているスタックトレース要素を確認することをお勧めします(main

メインスレッドが終了している場合は編集してください。これはダメです!

于 2009-06-02T14:50:21.193 に答える