首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >确定JVM的命令行根类

确定JVM的命令行根类
EN

Stack Overflow用户
提问于 2011-04-11 21:31:53
回答 3查看 437关注 0票数 2

当子类从超类继承main()时,是否可以确定在命令行上调用的实际类?例如,考虑以下两个类,其中A实现,由B继承

代码语言:javascript
复制
public class A {

    public static void main(String[] args) throws Exception {
        // Replace with <some magic here> to determine the class 
        //    invoked on the command-line
        final Class<? extends A> c = A.class;
        System.out.println("Invoked class: " + c.getName());

        final A instance = c.newInstance();
        // Do something with instance here...
    }
}

public class B extends A {
}

我们可以成功地调用B (即,B确实“继承”main --至少在任何意义上可以继承静态方法),但我没有找到确定用户调用的实际类的方法:

代码语言:javascript
复制
$ java -cp . A
Invoked class: A

$ java -cp . B
Invoked class: A

我最接近的是要求子类实现main(),并在超类中调用一个助手方法,然后该方法读取线程堆栈以确定调用类:

代码语言:javascript
复制
public class AByStack {

    public static void run(String[] args) throws Exception {
        // Read the thread stack to find the calling class
        final Class<? extends AByStack> c = (Class<? extends AByStack>)
            Class.forName(Thread.currentThread().getStackTrace()[2].getClassName());
        System.out.println("Invoked class: " + c.getName());

        final AByStack instance = c.newInstance();
        // Do something with instance here...
    }

    public static void main(String[] args) throws Exception {
        run(args);
    }
}

public class BByStack extends AByStack {

    public static void main(String[] args) throws Exception {
        // Call the master 'run' method
        run(args);
    }
}

这种方法有效:

代码语言:javascript
复制
$ java -cp . AByStack
Invoked class: AByStack

$ java -cp . BByStack
Invoked class: BByStack

但是我真的很想消除子类实现main()的要求(是的,请叫我挑食.)。我不介意它是否需要一些难看的代码,因为它将被实现一次并隐藏在基类中,而且我对Sun/Oracle很感兴趣,所以我愿意考虑使用私有的sun.misc类或类似的东西。

但我确实想避免平台依赖。例如,在Linux上,我们可以查看/proc/self/cmdline,但这当然不能移植到Windows (我不确定Mac我现在还没有带着我的Mac来测试这个技巧)。我认为JNI和JVMTI也是出于同样的原因离开的。关于JVMTI,我可能错了,但在我看来,它需要一个C包装器。如果不是,也许我们可以以某种方式使用这个接口。

这个问题是几年前在http://www.coderanch.com/t/375326/java/java/Getting-command-line-class上提出的。最好的答案是在每个子类中都需要一个静态初始化器块--对子类作者的要求不同,但类似于我演示的main调用run()解决方案。但我没有看到最近的讨论;我希望当前的‘m可能允许访问在讨论时无法获得的信息。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-04-15 21:57:40

由于我浪费了更多的时间来调查这个问题,所以我会在这里发表我的结论。

第一,重申这个问题的理据:

我已经将参数处理、I/O和其他共享任务抽象为一个抽象超类,我希望其他类能够对其进行扩展。在执行参数解析和共享设置之后,超类中的静态方法实例化子类的一个实例,并调用其run()方法。

子类的作者被鼓励实现公共静态void (String[]),并调用超类的主入口点。但是,与所有子类实现run()的要求不同,我们不能在编译时静态地执行该需求(因为抽象静态方法没有概念)。

因此,我试图在超类中实现一个main(String[])方法,该方法可以确定命令行上请求的子类的名称,并实例化相应的类。

我找到了两种方法,都是针对Sun / Oracle JVM的。

第一个使用内部sun.jvmstat类:

代码语言:javascript
复制
import java.lang.management.ManagementFactory;
import sun.jvmstat.monitor.MonitoredVmUtil;
import sun.jvmstat.monitor.VmIdentifier;
import sun.jvmstat.perfdata.monitor.protocol.local.LocalMonitoredVm;

...

public static String jvmstatMainClass() {
    // Determine the VMID (on most platforms, this will be the PID)
    final String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];

    // Connect to the virtual machine by VMID
    final VmIdentifier vmId = new VmIdentifier(pid);
    final LocalMonitoredVm lmVm = new LocalMonitoredVm(vmId, 1000);

    // Find the requested main-class
    String mainClass = MonitoredVmUtil.mainClass(lmVm, true);

    // And detach from the VM
    lmVm.detach();

    return mainClass;
}

第二种使用Sun的jps实用工具:

代码语言:javascript
复制
import java.io.BufferedReader;
import java.io.InputStreamReader;

...

public static String jpsMainClass() {
    // Determine the VMID (on most platforms, this will be the PID)
    final String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];

    // Execute the 'jps' utility
    final Process jps = Runtime.getRuntime().exec(new String[] { "jps", "-l" });
    final BufferedReader br = new BufferedReader(new InputStreamReader(jps.getInputStream()));

    // Parse the output of jps to find the current VM by PID
    for (String line = br.readLine(); line != null; line = br.readLine()) {
        final String[] split = line.split(" ");
        if (pid.equals(split[0])) {
                return split[1];
            }
        }
    }
    return null;
}

希望我浪费的时间能证明对别人有帮助。

票数 0
EN

Stack Overflow用户

发布于 2011-04-11 21:36:59

您可能希望这样做,以便可以使用不同的名称调用某些工具,但根据调用的名称,它们的行为大致相同吗?或者类似的魔法?

在这种情况下,您可以简单地在所有子类中拥有一个实际的main()方法,并将其委托给一个方法,该方法也使用被调用的类的名称:

代码语言:javascript
复制
public class Super {
    protected void doMain(String invokee, String... args) {
        System.out.println("I was invoked as: " + invokee);
    }
}

public class ToolA {
    public static void main(String... args) {
        new Super().doMain("ToolA", args);  // or ToolA.class.getName() to be refactor-proof
    }
}
票数 0
EN

Stack Overflow用户

发布于 2011-04-11 21:55:14

我想我应该修改run()方法的签名,而不是使用调用堆栈,而是在每个子类中实现main(),如下所示:

代码语言:javascript
复制
public class BByStack extends AByStack {

    public static void main(String[] args) throws Exception {
        // Call the master 'run' method
        run(BByStack.class, args);
    }
}

如果您所做的只是使用默认构造函数实例化,我甚至可以传入

new BByStack()

我不知道要做什么;这个方法是静态的,所以您不能获得this.getClass()之类的句柄;如果该方法没有在子类上定义,您也不能从堆栈中获得该信息。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5627819

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档