我想在运行时确定我的应用程序启动的类名,即具有 main() 方法的类名,但我在另一个线程中,并且我的堆栈跟踪并没有一直回到原始类。
我搜索了系统属性和 ClassLoader 必须提供的所有内容,但一无所获。这些信息不可用吗?
谢谢。
我想在运行时确定我的应用程序启动的类名,即具有 main() 方法的类名,但我在另一个线程中,并且我的堆栈跟踪并没有一直回到原始类。
我搜索了系统属性和 ClassLoader 必须提供的所有内容,但一无所获。这些信息不可用吗?
谢谢。
请参阅 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
我想到了。谁能告诉我这个环境变量是否总是存在于跨操作系统的其他 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.");
}
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";
}
尝试使用Thread.getAllStackTraces()。它从所有正在运行的线程返回堆栈跟踪的 Map,而不仅仅是当前线程。
鉴于澄清,我建议使用“从上面的参数化”成语。你有信息开始,保持它。
我确实为这样的东西放入了 RFE 4827318(六年前!),以便与测试运行者一起使用。
即使具有 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;
}
}`
怎么样:
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
编辑如果主线程已经退出,这不好!
我建议将此信息放入系统属性中。当您从脚本启动应用程序时,这通常很容易做到。
如果你不能这样做,我建议在每个应用程序的 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) {
}
}
这是我正在使用的,用于您不控制主要的情况:
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");
}
获取主类的另一种方法是在 Thread.getAllStackTraces 上查找该类,因此您甚至可以在 jar 中找到它,它适用于任何 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;
}
}