如何在线程java应用程序中确定运行时的主类?

时间:2009-06-02 14:37:44

标签: java runtime stack-trace classloader main

我想在运行时确定我的应用程序启动的类名,带有main()方法的类名,但是我在另一个线程中,我的堆栈跟踪不会一直回到原始类。

我搜索了系统属性以及ClassLoader必须提供的所有内容,并且没有提供任何内容。这些信息不可用吗?

感谢。

10 个答案:

答案 0 :(得分:6)

请参阅Tom Hawtin给出的链接评论。目前的解决方案是(仅限Oracle JVM):

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

仅使用Oracle Java 7进行测试。有关特殊情况的更多信息:http://bugs.java.com/view_bug.do?bug_id=4827318

答案 1 :(得分:4)

尝试使用Thread.getAllStackTraces()。它从所有正在运行的线程返回堆栈跟踪的Map,而不仅仅是当前线程。

答案 2 :(得分:3)

根据平台,JAVA_MAIN_CLASS环境值始终不存在。如果您只想获得" main"的名称启动Java进程的类,您可以这样做:

  public static String getMainClassName()
  {
    StackTraceElement trace[] = Thread.currentThread().getStackTrace();
    if (trace.length > 0) {
      return trace[trace.length - 1].getClassName();
    }
    return "Unknown";
  } 

答案 3 :(得分:2)

我明白了。任何人都可以告诉我这个环境变量是否总是在操作系统的其他Java实现中出现?这在Oracle JVM上产生类似“org.x.y.ClassName”

的字符串
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.");
}

答案 4 :(得分:2)

鉴于澄清,我建议使用“从上面参数化”的习语。你有信息开始,保持它。

我确实参加了一个RFE 4827318(六年前!)这样的事情用于测试跑步者。

答案 5 :(得分: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的堆栈跟踪元素

编辑如果主线程已经退出,这是不行的!

答案 6 :(得分: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) {
    }
}

答案 7 :(得分:0)

即使具有main()方法的线程已终止且您未使用Oracle JVM,您仍然可以尝试从操作系统获取信息。下面的代码获取了用于在Linux下启动JVM的命令行,但您可以为Windows编写一个版本等。然后,您可以查看JVM的参数以查找应用程序入口点。它可能直接在命令行上,或者您可能在指定jar的清单中查找Main-Class:classname。我首先使用System.getProperty(&#34; sun.java.command&#34;),朋友只有在必要时才会回到这个机制。

nullable

答案 8 :(得分:0)

以下是我正在使用的内容,适用于您无法控制主要内容的情况:

public static Class<?> getMainClass() {
  // find the class that called us, and use their "target/classes"
  final Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
  for (Entry<Thread, StackTraceElement[]> trace : traces.entrySet()) {
    if ("main".equals(trace.getKey().getName())) {
      // Using a thread named main is best...
      final StackTraceElement[] els = trace.getValue();
      int i = els.length - 1;
      StackTraceElement best = els[--i];
      String cls = best.getClassName();
      while (i > 0 && isSystemClass(cls)) {
        // if the main class is likely an ide,
        // then we should look higher...
        while (i-- > 0) {
          if ("main".equals(els[i].getMethodName())) {
            best = els[i];
            cls = best.getClassName();
            break;
          }
        }
      }
      if (isSystemClass(cls)) {
        i = els.length - 1;
        best = els[i];
        while (isSystemClass(cls) && i --> 0) {
          best = els[i];
          cls = best.getClassName();
        }
      }
      try {
        Class mainClass = Class.forName(best.getClassName());
        return mainClass;
      } catch (ClassNotFoundException e) {
        throw X_Util.rethrow(e);
      }
    }
  }
  return null;
}

private static boolean isSystemClass(String cls) {
  return cls.startsWith("java.") ||
      cls.startsWith("sun.") ||
      cls.startsWith("org.apache.maven.") ||
      cls.contains(".intellij.") ||
      cls.startsWith("org.junit") ||
      cls.startsWith("junit.") ||
      cls.contains(".eclipse") ||
      cls.contains("netbeans");
}

答案 9 :(得分:0)

Another way to get main class is look for that class on Thread.getAllStackTraces, so you could find it even inside jars, it works on any SDK (Open, Oracle...):

private static Class<?> mainClass = null;

public static Class<?> getMainClass()
{
    if (mainClass == null)
    {
        Map<Thread, StackTraceElement[]> threadSet = Thread.getAllStackTraces();
        for (Map.Entry<Thread, StackTraceElement[]> entry : threadSet.entrySet())
        {
            for (StackTraceElement stack : entry.getValue())
            {
                try
                {
                    String stackClass = stack.getClassName();
                    if (stackClass != null && stackClass.indexOf("$") > 0)
                    {
                        stackClass = stackClass.substring(0, stackClass.lastIndexOf("$"));
                    }
                    Class<?> instance = Class.forName(stackClass);
                    Method method = instance.getDeclaredMethod("main", new Class[]
                    {
                        String[].class
                    });
                    if (Modifier.isStatic(method.getModifiers()))
                    {
                        mainClass = instance;
                        break;
                    }
                }
                catch (Exception ex)
                {
                }
            }
        }
        return mainClass;
    }
}