Инициирующие поля в конструкторах с перехватчиками Byte Buddy - PullRequest
0 голосов
/ 03 мая 2019

Как я могу инициировать поля объекта в конструкторе-перехватчике?

Я создал конструктор с помощью Byte Buddy, как показано в следующем коде.

Class<?> klass = new ByteBuddy()
            .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)

            .defineProperty("origin", CoreObject.class, true)
            .defineProperty("variableNamedField", Map.class, true)

            .defineConstructor(Visibility.PUBLIC)
            .withParameters(CoreObject.class)
            .intercept(
                    // Invoke Objects default constructor explicitly
                    MethodCall.invoke(Object.class.getConstructor())
                            .andThen(FieldAccessor.ofField("origin").setsArgumentAt(0))
                            .andThen(FieldAccessor.ofField("variableNamedField").setsValue(new HashMap<>()))
                            .andThen(MethodDelegation.to(new FillMapInterceptor("variableNamedField")))

                    //
                    // I have to fill the map.
                    // Something like this:
                    //
                    // variableNamedField.put("first", new FirstHandler(origin));
                    // variableNamedField.put("second", new SecondHandler(origin));
                    //

            )
            .make()
            .load(CoreObject.class.getClassLoader())
            .getLoaded();

Сначала конструктор сохраняет параметр вличное поле.Затем он создает коллекцию.Затем он вызывает следующий перехватчик для заполнения этой коллекции.

class FillMapInterceptor {

    private final String mapField;

    public FillMapInterceptor(String mapField) {
        this.mapField = mapField;
    }

    public void construct(@FieldValue("variableNamedField") Map<String, Handler> map, @FieldValue("origin") CoreObject coreObject){
        map.put("first", new FirstHandler(coreObject));
        map.put("second", new SecondHandler(coreObject));
    }
}

Лучше создать экземпляр поля variableNamedField в перехватчике, поскольку выясняется, что variableNamedField полясоздается с тем же объектом HashMap каждый раз, когда создается новый экземпляр класса.Однако я могу передать существующее поле перехватчику только через аннотацию @FieldValue.Но кажется, что я не могу присвоить поле с новой переменной в перехватчике.

  1. Как инициировать поля класса в конструкторе перехватчика?Я могу только пропустить существующее значение поля с @FieldValue в перехватчике.
  2. Как сделать имя поля произвольным?Аннотации типа @FieldValue требуют постоянных параметров (например, имен полей).Может быть, я могу каким-то образом взять поле из результата метода defineProperty и передать его перехватчику или использовать в качестве поля перехватчика?
  3. Как передать параметры конструктора непосредственно в перехватчик?Мне не нравится, как сначала инициировать поле, а затем передать поле перехватчику с помощью аннотации @FieldValue.

1 Ответ

0 голосов
/ 04 мая 2019

Самый простой способ сделать это - определить конструктор, используя класс Advice. Класс совета позволяет вам определять код, используя шаблон, который позже будет встроен. В этом случае вы должны использовать @Advice.OnMethodExit, чтобы добавить код после Implementation, а затем обернуть вокруг кода, который вы уже создали выше:

Advice.to(YourAdvice.class).wrap(...)

Вы найдете всю информацию об определении совета в javadoc, по сути, вы можете скопировать вставленный выше код в статический метод, чтобы встроить его:

class YourAdvice {
  @Advice.OnMethodExit
  static void exit(@Advice.FieldValue("variableNamedField") 
        Map<Object, Object> variableNamedField) {
    variableNamedField.put("first", new FirstHandler(origin));
    variableNamedField.put("second", new SecondHandler(origin));
  }
}
...