Как использовать делегирование ByteBuddy для перехвата метода класса начальной загрузки - PullRequest
0 голосов
/ 09 апреля 2019

Я использую 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 по-прежнему говорит, что преобразование завершено? Как получить правильный морфинг для этого случая? Спасибо!

1 Ответ

0 голосов
/ 11 апреля 2019

Я предполагаю, что ваша ретрансформация уже терпит неудачу. Вы пытались добавить слушателя в процесс ретрансформации. Что означает Исключение: java.lang.Exception, выброшенное из UncaughtExceptionHandler в потоке "main" означает?

Одна вещь, которую я заметил, это то, что вы не настраиваете график модуля. Модуль java.base не сможет увидеть ваш перехватчик, который, скорее всего, загружен в неназванный модуль загрузчика начальной загрузки. Вы пытались добавить assureReadEdgeTo в свое преобразование, где вы указываете на свой класс перехватчика?

Также обратите внимание, что Advice позволяет вам пропустить или даже повторить выполнение метода. Посмотрите на Javadoc методов входа или выхода. Как правило, при инструктаже классы начальной загрузки советы оказываются более надежными.

...