これらの不要な印刷を永久に取り除く方法は、バイトコード操作を使用して、厄介なライブラリから印刷ステートメントを削除することです。これは、たとえばASM (または他の高レベルで使いやすいAOPフレームワークのいずれか) を使用して実行できます。
これは、実行時に行うことも、ライブラリのクラス ファイルを書き換える 1 回限りの操作として行うこともできます。方法については、ASM のドキュメントを参照してください。これが概念実証です。System.out
それが行うことは、すべての参照を、何もしないa への参照に置き換えることPrintStream
です。
まずテスト。私のプロジェクトのいくつかのユーティリティ クラスを使用して、バイトコード変換のテストを支援します (テストには、カスタム クラス ローダーを作成し、バイトコード変換を適切なクラスに適用する必要がありますが、他のクラスには適用しません)。
package net.orfjackal.dimdwarf.aop;
import net.orfjackal.dimdwarf.aop.conf.*;
import org.junit.*;
import org.objectweb.asm.*;
import org.objectweb.asm.util.CheckClassAdapter;
import java.io.*;
import java.lang.instrument.ClassFileTransformer;
import java.lang.reflect.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
public class RemoveCallsToSystemOutTest {
private PrintStream originalOut;
private ByteArrayOutputStream collectedOut;
@Before
public void collectSystemOut() {
originalOut = System.out;
collectedOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(collectedOut));
}
@After
public void restoreOriginalSystemOut() {
System.setOut(originalOut);
}
@Test
public void the_target_class_prints_when_not_manipulated() throws Exception {
String safetyCheck = callPrintSomething(TroublesomePrinter.class);
assertThat(safetyCheck, is("it did execute"));
assertThat(collectedOut.size(), is(greaterThan(0)));
}
@Test
public void the_target_class_does_not_print_when_it_has_been_manipulated() throws Exception {
String safetyCheck = callPrintSomething(instrumentClass(TroublesomePrinter.class));
assertThat(safetyCheck, is("it did execute"));
assertThat(collectedOut.size(), is(0));
}
private static String callPrintSomething(Class<?> clazz) throws Exception {
Method m = clazz.getMethod("printSomething");
m.setAccessible(true);
return (String) m.invoke(null);
}
private static Class<?> instrumentClass(Class<?> cls) throws ClassNotFoundException {
ClassFileTransformer transformer = new AbstractTransformationChain() {
protected ClassVisitor getAdapters(ClassVisitor cv) {
cv = new CheckClassAdapter(cv);
cv = new RemoveCallsToSystemOut(cv);
return cv;
}
};
ClassLoader loader = new TransformationTestClassLoader(cls.getPackage().getName() + ".*", transformer);
return loader.loadClass(cls.getName());
}
}
class TroublesomePrinter {
public static String printSomething() {
System.out.println("something");
return "it did execute";
}
}
そして実装です。このコードを理解せずに使用しないでください。偶然にプログラムしないでください。
class SilentSystem {
public static final PrintStream out = new PrintStream(new OutputStream() {
public void write(int b) {
}
});
}
class RemoveCallsToSystemOut extends ClassAdapter {
public RemoveCallsToSystemOut(ClassVisitor cv) {
super(cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new MyMethodAdapter(super.visitMethod(access, name, desc, signature, exceptions));
}
private static class MyMethodAdapter extends MethodAdapter {
public MyMethodAdapter(MethodVisitor mv) {
super(mv);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (opcode == Opcodes.GETSTATIC
&& owner.equals("java/lang/System")
&& name.equals("out")
&& desc.equals("Ljava/io/PrintStream;")) {
super.visitFieldInsn(opcode, "net/orfjackal/dimdwarf/aop/SilentSystem", name, desc);
} else {
super.visitFieldInsn(opcode, owner, name, desc);
}
}
}
}