Как украсить существующий Java метод объекта? - PullRequest
0 голосов
/ 23 января 2020

РЕДАКТИРОВАТЬ: я описал наше решение на { ссылка }

У меня есть java объект. Это экземпляр одного из многих подклассов, расширяющих абстрактный класс. Я хотел бы изменить один из его методов так, чтобы он выполнял некоторый дополнительный код перед вызовом исходного метода. Моя цель концептуально такая же, как pointcut в AspectJ.

Хорошо, если я создам некоторую модифицированную версию исходного объекта, а не изменю оригинал. Также хорошо, если решение включает в себя манипулирование байт-кодом.

Предыдущая работа

Я рассмотрел вопрос создания прокси через JavaAssist. Проблема в том, что метод create ProxyFactory ожидает, что я заранее знаю типы ввода конструктора. Я не. Я могу создать свой объект, не вызывая конструктор через Objenesis, но тогда полученный прокси-объект будет иметь нулевые значения для любых значений, установленных конструктором. Это означает, что мой результирующий объект будет вести себя не так, как исходный, всякий раз, когда на значение, установленное конструктором, прямо ссылаются.

Контекст

Мы используем Flink через AWS Kinesis Аналитика данных для преобразования некоторых потоковых данных. Мы хотели бы включить общий код в начало всех наших StreamOperator open () методов без необходимости изменения каждого оператора. Одним из вариантов использования этого является обеспечение того, чтобы пользовательский агент метрик работал в каждом экземпляре, на котором работает оператор.

Ответы [ 4 ]

1 голос
/ 23 января 2020

С помощью Byte Buddy вы можете создать оболочку или агента Java, который может достичь этой цели. Если вы боретесь с вызовом конструктора класса-оболочки, такая же проблема, однако, может возникнуть при использовании Byte Buddy, поскольку любая библиотека связана с ограничениями, заданными JVM.

Чтобы создать агент Java, используйте AgentBuilder. Затем вы можете указать все типы для перехвата, используя шаг type, например, все типы, которые реализуют определенный интерфейс или расширяют класс. Для transform, Byte Buddy предлагает API декорации метода, называемый Advice, он позволяет вам добавить дополнительный код, такой как:

class MyAdvice {
  @Advice.OnMethodEnter
  static void enter() { System.out.println("Hello"); }
}

с помощью

builder = builder.visit(Advice.to(MyAdvice.class).on(named("foo")));

, который вы можете, например, напечатайте hello world в начале всех методов с именем "foo" для указанных вами типов. Вы можете узнать больше о Java агентах в документации пакета для java.instrument пакета .

0 голосов
/ 15 февраля 2020

Ответ от исходного автора: мы решили проблему, создав прокси-сервер ByteBuddy для StreamExecutionEnvironment, который перехватывал вызовы getStreamGraph и преобразовывал (используя отражение) каждый узел jobVertexClass в класс, который расширял исходный тип класса, но включал наши пользовательские логи c. Поскольку разные классы требуют разных параметров, мы создали экземпляр прокси без вызова конструктора с помощью Objenesis. Чтобы решить проблему с закрытыми полями, обычно устанавливаемыми в конструкторе, и оставить его пустым, мы использовали отражение, чтобы изменить видимость всех закрытых полей, а затем скопировали каждое значение поля из исходного объекта в прокси-объект.

Мы сделали не следует использовать решение агента, предложенное Рафаэлем Винтерхальтером, поскольку для этого требуется возможность запуска кода установки агента на каждом рабочем экземпляре, что аналогично исходной проблеме желания запустить агент метрик на каждом рабочем компьютере. Хотя я не указал этого в своем первоначальном вопросе, код, создающий прокси-объекты, создается на машине управления заданиями Flink, а не на рабочих машинах.

0 голосов
/ 28 января 2020

Прежде всего, я бы подал запрос функции на AWS для поддержки вашего варианта использования. Это было бы самым чистым решением.

Во-вторых, я бы не стал искать способ перезаписать open(). Поскольку вы находитесь в среде, в которой у вас нет большого контроля, я мог бы себе представить, что подходы либо не работают вообще, либо работают на agile и не работают с обновлением среды.

Я бы сделал ленивая инициализация в соответствующих методах UDF и, конечно, это в некотором общем методе полезности.

private Counter counter;

@Override
public Integer map(String value) {
    if (counter == null) {
        RuntimeContext ctx = getRuntimeContext();
        counter = ctx.getMetricGroup().counter("outputs");
    }
    counter.inc();
    return Integer.parseInt(value);
}
0 голосов
/ 23 января 2020

Решение Flink-Speci c может заключаться в реализации пользовательских версий операторов Flink, которые вы используете. Я не уверен, что это приведет вас к хорошему месту; просто поделитесь идеей на тот случай, если она будет полезна.

Существует не так много документации о том, как реализовать пользовательские операторы, но был разговор Flink Forward по этой теме c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...