我有一个Java应用程序,它的主函数是从带有2个参数的批处理文件中调用的。 现在我想将另一对参数传递给同一个应用程序,而不是打开另一个实例。 是否可以从外部调用正在运行的 jar 文件的主函数(或任何其他函数)?
感谢您的帮助!
最佳答案
正如我在评论中提到的,您可能应该使用 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();
}
这是一种扭曲的生产者/消费者模式。
消费者太无聊了,它吐槽String
如果队列被中断或收到String
,则从队列中退出并退出“导出”。另一方面,制作人则更有趣一些。
首先我们有interface ProducerMBean
,这是一个public interface
正如我们稍后将看到的,它充当面向外的交互点。然后我们有一个 Producer
其中implements ProducerMBean
,它只是将内容添加到 BlockingQueue
。这里值得注意的是,所有这些必须是线程安全的,否则您将遇到 JMX 线程将您的方法异步插入到应用程序线程的问题。
然后,我们向平台注册我们的 MBean(管理 bean),这会公开 interface
到外面会;我们需要小心我们公开的内容,因为我们公开的任何内容都可以被调用。 ObjectName
的格式是 <folder>:type=<name>
,稍后你就会明白我的意思。
现在,当应用程序启动时,它就像预期的那样挂起。为了发送Strings
我们需要打开 jconsole 并 Hook 到 PID
<folder>
ObjectName
的一部分成为顶级目录,<name>
ObjectName
的一部分成为 MBean 名称本身。您现在可以使用add
发送应用程序的方法String
s 并查看它打印它们。很好,但是我们如何从命令行调用它?
首先,我们需要将 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:<port>
您将直接连接到应用程序的 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:<port>
上的 JMX 服务器然后获取与我们之前注册的同名的 MBean。
我们现在可以调用该 MBean 上的方法,将命令发送到其他应用程序。
显然,您需要重写帮助程序应用程序来读取命令行参数,然后您可以调用类似的内容
java -cp MyMainApp.jar:MyHelperApp.jar com.example.helper.Main add TEST EXIT
从您的脚本将参数传递给主 Java 程序。
关于java - 调用批处理运行jar的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17359546/