Способ избавиться от этих нежелательных отпечатков навсегда будет использовать манипулирование байт-кодом , чтобы удалить операторы печати из проблемной библиотеки.Это может быть сделано, например, с использованием ASM (или другого более высокого уровня, более простого в использовании AOP frameworks).
Вы можете сделать это либо во время выполнения, либокак одноразовая операция переписывания файлов классов библиотеки.Обратитесь к документации ASM, чтобы узнать как.Вот подтверждение концепции.Он заменяет все ссылки на System.out
ссылкой на 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);
}
}
}
}