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

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

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

谢谢。

请参阅Tom Hawtin给出的评论。 这些天的解决方案是(仅限Oracle JVM):

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

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

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

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

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

根据平台,JAVA_MAIN_CLASS环境值并不总是存在。 如果您只想获得启动Java进程的“main”类的名称,则可以执行以下操作:

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

我想到了。 任何人都可以告诉我,这个环境变量是否会在操作系统的其他Java实现中始终存在? 这在Oracle JVM上产生类似“org.xyClassName”的字符串

 public static String getMainClassName() { for (final Map.Entry 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."); } 

怎么样的:

 Map 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

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

我建议将此信息放入系统属性中。 从脚本启动应用程序时,这通常很简单。

如果你不能这样做,我建议在每个应用程序的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) { } } 

即使具有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//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; } }` 

这是我正在使用的,对于你不控制主要的情况:

 public static Class getMainClass() { // find the class that called us, and use their "target/classes" final Map traces = Thread.getAllStackTraces(); for (Entry 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"); } 

获取主类的另一种方法是在Thread.getAllStackTraces上查找该类,因此您甚至可以在jar中找到它,它适用于任何SDK(Open,Oracle …):

 private static Class mainClass = null; public static Class getMainClass() { if (mainClass == null) { Map threadSet = Thread.getAllStackTraces(); for (Map.Entry 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; } }