Я использую java-агент и bytebuddy для перехвата методов «чтения» и «записи» в FileIOStreams. Функция, которую нужно реализовать, - это «вызывать оригинальные методы при определенных обстоятельствах, иначе передать». В связи с этим мне нужно иметь полный контроль над вызывающим потоком, используя делегирование метода, а не оборачивать его с помощью Advice.
Перехват метода работает нормально, когда @Morph отсутствует, но он не работает, когда я добавляю @Morph к параметрам. Я проверил с некоторыми другими аннотациями:
добавив @AllArguments, @This не заблокирует делегирование, метод будет работать как мой перехватчик;
добавив @Morph, @SuperCall заблокирует делегирование. Не будет выброшено исключение: оригинальный метод будет работать так, как раньше.
Вот код, который я хочу реализовать:
public static void mountAgent(Instrumentation inst) {
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.ignore(new AgentBuilder.RawMatcher.ForElementMatchers(nameStartsWith("net.bytebuddy.").or(isSynthetic()), any(), any()))
.with(new AgentBuilder.Listener.Filtering(
new StringMatcher("java.io.FileInputStream", StringMatcher.Mode.EQUALS_FULLY)
.or(new StringMatcher("java.io.FileOutputStream", StringMatcher.Mode.EQUALS_FULLY)),
AgentBuilder.Listener.StreamWriting.toSystemOut()))
.type(named("java.io.FileOutputStream"))
.transform(new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassLoader classLoader,
JavaModule module) {
return builder.method(named("write").and(not(isNative())).and(takesArgument(0, byte[].class)))
.intercept(MethodDelegation
.withDefaultConfiguration()
.withBinders(Morph.Binder.install(Morphing.class))
.to(WriteInterceptor.class));
}})
.installOn(inst);
}
(Код для добавления перехватчиков в BootstrapClassLoaderSearch пропускается)
И вот мои перехватчики:
public interface Morphing<T> {
T Object invoke(Object[] agrs);
}
@SuppressWarnings("unused")
public static class WriteInterceptor {
@RuntimeType
public static void write(
//change the input here
byte[] bytes,
@AllArguments Object[] args,
@Morph Morphing<Void> morphing
) throws Exception {
if (true) {
morphing.invoke(args);
}
else {
// do something
throw new Exception();
}
}
}
Если ввод перехватывающей функции пуст или содержит только байты [] байтов, делегирование сработает, и будет выдано исключение:
[Byte Buddy] IGNORE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] DISCOVERY java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] TRANSFORM java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileOutputStream [null, module java.base, loaded=true]
Exception: java.lang.Exception thrown from the UncaughtExceptionHandler in thread "main"
Если ввод
byte [] bytes, @AllArguments Object [] args, @Morph Morphing morphing
или
@ AllArguments Object [] args, @Morph Morphing morphing
вызывается встроенная функция записи и вывод
[Byte Buddy] IGNORE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileInputStream [null, module java.base, loaded=true]
[Byte Buddy] DISCOVERY java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] TRANSFORM java.io.FileOutputStream [null, module java.base, loaded=true]
[Byte Buddy] COMPLETE java.io.FileOutputStream [null, module java.base, loaded=true]
В чем причина того, что делегирование не работает после добавления @Morph, но bytebuddy по-прежнему говорит, что преобразование завершено? Как получить правильный морфинг для этого случая? Спасибо!