如何从 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");
也许这会对你有所帮助:)
编辑:我把它换了个方向,让带前缀的字符串进入正常的外流
我是运行这样的外部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");
也许这会对你有所帮助:)
编辑:我把它换了个方向,让带前缀的字符串进入正常的外流