【问题标题】:Call a method of running jar from batch从批处理中调用运行jar的方法
【发布时间】:2013-06-25 22:08:12
【问题描述】:

我有一个 Java 应用程序,它的主函数是从一个带有 2 个参数的批处理文件中调用的。 现在我想将另一对参数传递给同一个应用程序,而不是打开另一个实例。 是否可以从外部调用正在运行的 jar 文件的主函数(或任何其他函数)?

感谢您的帮助!

【问题讨论】:

  • 您可以为此使用JMX。或者,您可以在应用程序中打开一个服务器套接字并通过它发送数据。最不可靠的方法是使用文件进行数据交换。

标签: java function batch-file jar main


【解决方案1】:

正如我在评论中提到的,您可能应该使用 JMX 来执行此操作。

JMX 允许您从您的程序中公开“管理 bean”,然后可以远程调用。

让我们从一个简单的例子开始:

private static class Consumer implements Runnable {

    private final BlockingQueue<String> blockingQueue;

    public Consumer(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                final String s = blockingQueue.take();
                System.out.println(s);
                if ("EXIT".equalsIgnoreCase(s)) {
                    return;
                }
            } catch (InterruptedException ex) {
                return;
            }
        }
    }
}

public static interface ProducerMBean {

    void add(String string);

    String contents();
}

private static class Producer implements ProducerMBean {

    private final BlockingQueue<String> blockingQueue;

    public Producer(BlockingQueue<String> blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void add(String string) {
        blockingQueue.add(string);
    }

    @Override
    public String contents() {
        return blockingQueue.toString();
    }
}

public static void main(String[] args) throws Exception {
    final ExecutorService executorService = Executors.newSingleThreadExecutor();
    final BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
    executorService.submit(new Consumer(blockingQueue));
    final Producer producer = new Producer(blockingQueue);
    final ObjectName name = new ObjectName("com.example.producer:type=SomeUnqiueName");
    ManagementFactory.getPlatformMBeanServer().registerMBean((ProducerMBean) producer, name);
    executorService.shutdown();
}

这是一种扭曲的生产者/消费者模式。

消费者非常无聊,它从队列中吐出Strings,如果它被中断或收到String“退出”,它就会退出。另一方面,制作人更有趣。

首先我们有interface ProducerMBean,这是一个public interface,作为我们稍后会看到的向外的交互点。然后我们有一个Producer 哪个implements ProducerMBean,它只是将东西添加到BlockingQueue。值得注意的是,所有这些必须是线程安全的,否则您将遇到 JMX 线程将您的方法与应用程序线程异步插入的问题。

然后我们向平台注册我们的 MBean(管理 bean),这会将 interface 暴露给外部;我们需要小心我们暴露的东西,因为我们暴露的任何东西都可以被调用。 ObjectName 的格式是 &lt;folder&gt;:type=&lt;name&gt;,稍后你会明白我的意思。

所以现在,当应用程序启动时,它就像预期的那样挂起。为了发送Strings,我们需要打开 jconsole 并挂钩到 PID

ObjectName&lt;folder&gt; 部分成为顶级目录,ObjectName&lt;name&gt; 部分成为 MBean 名称本身。您现在可以使用add 方法发送应用程序Strings 并查看它打印它们。很不错,但是我们如何从命令行调用它呢?

首先,我们需要将 MBean 服务器注册到一个端口而不是 PID,这是通过使用以下 JVM 参数调用应用程序来完成的:

-Dcom.sun.management.jmxremote.port=<port> \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote=true

现在,如果您运行jconsole localhost:&lt;port&gt;,您将直接连接到应用程序的 MBean 服务器。魔法。

这仍然没有达到我们想要的效果。有一个 SO answer 处理对 JMX 服务器的命令行访问,但我更喜欢简单地使用另一个助手应用程序。

此应用程序将启动,从命令行读取所有参数,然后调用使用给定参数指定的 MBean 方法。

这是一个简单的例子

public static void main(String[] args) throws Exception {
    final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:<port>/jmxrmi");
    final JMXConnector jmxc = JMXConnectorFactory.connect(url);
    final MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
    final ObjectName name = new ObjectName("com.example.producer:type=SomeUnqiueName");
    final ProducerMBean mbeanProxy = JMX.newMBeanProxy(mbsc, name, ProducerMBean.class, true);
    mbeanProxy.add("TEST");
    mbeanProxy.add("EXIT");
}

这个应用程序需要在类路径上有interface ProducerMBean,最简单的方法是在类路径上运行带有主应用程序的助手。我们连接到在localhost:&lt;port&gt; 上运行的 JMX 服务器,然后获取与我们之前注册的名称相同的 MBean。

我们现在可以调用该 MBean 上的方法,向其他应用程序发送命令。

显然你需要重写帮助应用程序来读取命令行参数,然后你可以调用类似的东西

java -cp MyMainApp.jar:MyHelperApp.jar com.example.helper.Main add TEST EXIT

从您的脚本中将参数传递给主 Java 程序。

【讨论】:

  • 哇,这是一个很好的答案。感谢你的付出!我希望我可以在我的应用程序中实现它。
【解决方案2】:

您需要监听来自外部世界的数据。例如,它可以是网络端口或控制台。创建另一个线程,当收到一些数据时,它会监听并运行东西。您还可以使用反射来调用任意方法(不仅是预定义的)。

【讨论】:

    【解决方案3】:

    是的,您可以,再创建一个批处理文件并将您的不同参数传递给:

    public static void main(String arg[]) 
    

    作为主要方法的签名接受数组字符串。尽可能多地传递,并且你的 jar 支持。

    【讨论】:

    • 这就是我所做的,但是当我再次调用它时,会调用另一个实例。我想像以前一样通过它..
    • 然后你应该创建一个对象引用并存储早期的实例,并在需要时从存储中使用它。
    • 您可能无法访问实际实例,但可以在新对象中操作数据。详情请参考:stackoverflow.com/questions/14741338/…
    【解决方案4】:

    为什么不将方法本身的名称作为参数传递给主类并构建逻辑以在您的主类中执行该方法?

    【讨论】:

      猜你喜欢
      • 2011-02-07
      • 2020-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-11
      • 2011-01-04
      相关资源
      最近更新 更多