在Java中,即使main()没有';你救不了他们吗?

在Java中,即使main()没有';你救不了他们吗?,java,command-line-arguments,Java,Command Line Arguments,我们有一个带有main()的程序,它解析某些CLP,但不将它们保存在任何地方。 然后,我有自己的插件代码,需要访问原始CLP(以便可以传输更多参数)。但是,我无法更改main() ,我正在寻找Linux上的等效Java解决方案 更新:显然,我知道main()是如何工作的。不幸的是,我无法更改现有应用程序或其调用方式(CLP除外)。我只能通过沙盒插件代码访问。我的问题是,是否有办法获取调用JVM的命令行(而不是带-D的环境变量)。创建自己的主类。保存参数。调用旧的main 在命令行上使用Syste

我们有一个带有main()的程序,它解析某些CLP,但不将它们保存在任何地方。 然后,我有自己的插件代码,需要访问原始CLP(以便可以传输更多参数)。但是,我无法更改main()

,我正在寻找Linux上的等效Java解决方案


更新:显然,我知道main()是如何工作的。不幸的是,我无法更改现有应用程序或其调用方式(CLP除外)。我只能通过沙盒插件代码访问。我的问题是,是否有办法获取调用JVM的命令行(而不是带-D的环境变量)。

创建自己的主类。保存参数。调用旧的
main


在命令行上使用
System.getProperty
-Dkey=value
(在主类名或
-jar
之前)可能会更容易。

一旦您意识到Java的主方法只是另一个以字符串数组为参数的静态方法,那么解决方案就简单了

创建一个存储CLP的新类,然后调用旧类。稍后,您可以使用新类访问CLP:

import NotToBeChangedMainClass;

public MyMainClass {
  public static final String[] ARGUMENTS;
  public static void main(String ... args) {
    ARGUMENTS = args;
    NotToBeChangedMainClass.main(args);
  }
}


最后,将任何外部调用方(例如任何批处理文件)更改为使用MyMainClass而不是NotToBeChangedMainClass。如果您使用的是可运行JAR或类似的东西,这就需要更改相应的配置文件。

除了以某种方式在main中执行之外,我认为您唯一的其他选择是转到操作系统级别并执行一些命令以获取参数

public aspect StoreArgumentsOfMain {

  /**
   * This pointcut intercepts all calls to methods called main with a string array as
   * argument.
   */
  pointcut mainMethod(String[] arguments): execution(void main(String[])) && args(arguments);

  /**
   * Before the original main method gets called, store the arguments in the registry.
   */
  before(String[] arguments): mainMethod(arguments) {
    System.out.println("Storing arguments");
    ArgumentRegistry.ARGS = arguments;
  }

}
在linux上,运行进程的cmd行参数存储在/proc/pid/cmdline

因此,要获取它们,您必须找到进程id。请参见此处:

然后使用这个open/proc/pid/cmdline并解析它。此文件的格式和c语言示例如下:

最好将这两个调用封装在从java调用的一个shell脚本中


请注意,这将是非常不可移植的,是有点黑客。但是如果需要…

如果您没有任何选择,那么您必须保留所有现有类名及其确切名称(根据我之前的回答),然后您必须使用AspectJ

让我们考虑一下这个类:

public class UnmodifyableClassWithMain {
  public static void main(String[] args) {
    System.out.println("In main");
    new ClassUsingArgumentRegistry();
  }
}

首先,您需要保存一些包含命令行参数的内容。为了简单起见,我将使用带有静态字段的简单类:

public class ArgumentRegistry {
  public static String[] ARGS;
}
然后,您需要定义一个方面来拦截对main的调用并存储参数

public aspect StoreArgumentsOfMain {

  /**
   * This pointcut intercepts all calls to methods called main with a string array as
   * argument.
   */
  pointcut mainMethod(String[] arguments): execution(void main(String[])) && args(arguments);

  /**
   * Before the original main method gets called, store the arguments in the registry.
   */
  before(String[] arguments): mainMethod(arguments) {
    System.out.println("Storing arguments");
    ArgumentRegistry.ARGS = arguments;
  }

}
为了进行尝试,我还使用ArgumentRegistry创建了一个类:

public class ClassUsingArgumentRegistry {

  public ClassUsingArgumentRegistry() {
    System.out.println("Arguments: " + java.util.Arrays.toString(ArgumentRegistry.ARGS));
  }

}

就这样。如果启用AspectJ的编译时编织并使用“java UnmodifyableClassWithMain Foo Bar Baz”运行结果,我将得到以下输出:

Storing arguments
In main
Arguments: [foo, bar, baz]

注意,这个解决方案非常有限,因为Linux截断了它保存的命令行。由于Java命令行通常有很长的类路径,这是一个非常现实的问题

下面是实现Pablojim给出的答案的Java代码

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Test {
  public static List<String> getLinuxCmdline(int maxBytesToRead) throws IOException {
    List<String> result = new ArrayList<>();
    String pid = new File("/proc/self").getCanonicalFile().getName();
    File cmdlineFile = new File("/proc/" + pid + "/cmdline");
    final int growBy = 1024;
    try (FileInputStream is = new FileInputStream(cmdlineFile);) {
      byte[] data = new byte[Math.min(growBy, maxBytesToRead)];
      int totalRead = 0; 
      while (totalRead < maxBytesToRead) {
        int read = is.read(data, totalRead, data.length - totalRead);
        if (read > 0) {
          totalRead += read;
          if (data.length == totalRead) {
            data = Arrays.copyOf(data, Math.min(data.length + growBy, maxBytesToRead));
          }
        } else {
          break;
        }
      }
      int start = 0;
      int scan = 0;
      while (scan < totalRead) {
        if (data[scan] == 0) {
          result.add(new String(Arrays.copyOfRange(data, start, scan)));
          start = scan + 1;
        }
        scan++;
      }
      if (scan - start > 0) result.add(new String(Arrays.copyOfRange(data, start, scan)));        }
    return result;
  }

  public static void main(String[] args) throws IOException {
    System.out.println(getLinuxCmdline(Integer.MAX_VALUE));
  }
}

我在java.lang.Runtime或System中没有看到任何内容。我猜.NET使用特定于操作系统的方法来检索当前进程的CLP。不幸的是,我无法更改main()。。。使用-D执行此操作也是一个问题,因为我只能控制标准CLP…:(显然,我知道main()是如何工作的。不幸的是,我无法更改现有应用程序或其调用方式(CLP除外)。我只能通过沙盒插件代码进行访问。请注意,此解决方案受到限制,因为Linux截断了它保存的命令行。(我相信它是可配置的,但默认情况下只有几个K。)由于Java命令行通常由于包含类路径而非常长,所以很容易达到这一限制。