如何从 class 加载程序加载的 jar 中获取系统输出控制台?

How to get System out console from a jar loaded by a class loader?

我是运行这样的外部jar插件:

  Class<?> pluginClass = pluginLoader.loadClass(".......");     
  Method main = pluginClass.getMethod("main", String[].class);
  main.invoke(null, new Object[] { new String[0] }); 

效果很好。现在需要将插件控制台消息保存到字符串

  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  PrintStream ps = new PrintStream(baos); // how to indicate System.out coming from JAR plugin 
  System.setOut(ps); 
 // how to connect ps with plugin only 

但此代码将所有控制台消息保存到字符串中。我不需要所有应用程序消息。如何重定向 plugin only 消息......来自这个加载的 jar 的消息到字符串中?

System.out 是每个进程,没有办法让每个 class 加载器有不同的流。如果您迫切需要从插件中取出系统,有 2 个选项: 1. 如果您有权访问其代码并使插件使用此流,则将输出流传递给您的插件。 2. 运行 你的插件作为外部进程。这样你就可以重定向它的输出。 另一种选择:如果您可以区分插件输出,则可以实现自己的路由输出流并将其设置为系统输出。

你做不到你要求的。进程中只有一个标准输出流,它与插件代码和您的代码共享。

您可以 运行 将插件代码作为一个单独的进程并捕获输出流。您可以使用 "java.home" 系统 属性 找到启动您的进程的 JRE 的位置,并使用它来形成命令行来启动插件 jar。

https://docs.oracle.com/javase/8/docs/api/java/lang/ProcessBuilder.html

我做了这个解决方法:

public class CustomPrintStream extends PrintStream {
    private String prefix;

    public CustomPrintStream(String prefix, OutputStream out) {
        super(out);
        this.prefix = prefix;
    }

    @Override
    public void println(String s) {
        if(s.startsWith(prefix))
            super.println(s);
        else {
            System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));
            System.out.println(s);
            System.setOut(this);
        }
    }
}

这使您可以为每个主程序添加前缀 System.out.printlns,以便它们正常执行。那些没有前缀的(来自你的插件)直接进入定义的输出流(在我的例子中是文件输出流)

它是这样使用的:

System.setOut(new CustomPrintStream("test", new FileOutputStream("C:\out.txt"))); //Of course you can also use ByteArrayOutputStream, as you did before
System.out.println("test 1"); //this goes into the standard outstream
System.out.println("2"); //and this goes into the fileoutputstream
System.out.println("test 3");

也许这会对你有所帮助:)

编辑:我把它换了个方向,让带前缀的字符串进入正常的外流