Способа избавиться от этих нежелательных отпечатков постоянно будет использовать байткод манипуляции удалить операторы печати из беспокойной библиотеки. Это можно сделать, например, используя ASM (или один из других более высоких уровней и более простой в использовании каркасы AOP).
Вы можете сделать это либо во время выполнения, либо в виде однократной операции перезаписи файлов классов библиотеки. Обратитесь к документации ASM, чтобы узнать, как это сделать. Вот доказательство концепции. Что он делает, так это то, что он заменяет все ссылки на System.out
ссылкой на PrintStream
, который ничего не делает.
Сначала испытания. Они используют некоторые служебные классы от my project, чтобы помочь в тестировании преобразований байт-кода (для тестирования требуется создание пользовательского загрузчика классов и применение преобразований байт-кода в нужный класс, но не любые другие классы).
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";
}
}
И затем реализация. Обратите внимание, что вы не должны использовать этот код, не понимая его. Не следует program by coincidence.
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);
}
}
}
}
Так что просто выбросьте его в случайный файл? А потом удалите файл? Звучит немного расточительно, разве не лучше сбросить его в буфер памяти? – Oak 2010-11-25 22:12:43
@Oak Если вас не интересуют сообщения - как продлить PrintStream и игнорировать все (пустые реализации)? – stacker 2010-11-25 22:32:23
Подклассификация с пустой реализацией на самом деле является лучшей идеей до сих пор - очень простая, довольно короткая с анонимным классом. – Oak 2010-11-25 22:33:57