Реализация методов в ByteBuddy для изменения полей во время выполнения - PullRequest
0 голосов
/ 01 марта 2020

Я использую Byte Buddy для создания классов bean-подобных интерфейсов во время выполнения, сокращая тонны стандартного кода. Создать поля из геттеров и сеттеров тривиально, но я хочу больше использовать Byte Buddy. Начнем с того, что я не хочу писать такой код везде:

    if (obj.getMax() < obj.getMin()) {
        int temp = obj.getMax();
        obj.setMax(obj.getMin());
        obj.setMin(temp);
    }

    obj.setMax(obj.getMax() + 1);
    int currentMax = getMax();

, когда я могу просто сделать:

    obj.enforceMinMaxOrder();
    int currentMax = obj.incrementAndGetMax();

Вот сокращенный пример. Как я могу использовать Byte Buddy, чтобы две вышеупомянутые работы работали? Предположительно с использованием класса перехватчика, но я не сталкивался с примерами, которые модифицируют поля. Важные вещи:

  • поля неизвестны во время компиляции, они будут извлечены из имен методов (т. Е. Имя метода «forceceMinMaxOrder» использует «min» и «max» в качестве имен полей)
  • Я бы хотел избежать отражения во время выполнения, используемого после того, как Byte Buddy генерирует классы, но удерживать ссылки на поля или методы и вызывать их хорошо.

    DynamicType.Builder<? extends Object> builder = new ByteBuddy().subclass(Object.class);
    builder = builder.implement(MyInterface.class);
    builder = builder.method(
        (isGetter().or(isSetter())))
        .intercept(FieldAccessor.ofBeanProperty());
    for (Method m : MyInterface.class.getMethods()) {
        String methodName = m.getName();
        Class<?> t = m.getReturnType();
        if (t != void.class && methodName.startsWith("get")) {
            String fieldName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
            builder = builder.defineField(fieldName, t, Visibility.PUBLIC);
            builder = builder.method(named(fieldName)).intercept(FieldAccessor.ofField(fieldName));
        }
    }
    
    MyInterface obj = (MyInterface) builder.make().load(BeanUtil.class.getClassLoader()).getLoaded().getConstructor().newInstance();
    
    obj.setMin(10);
    obj.setMax(5);
    
    obj.incrementAndGetMin();
    obj.ensureMinMaxOrder();
    // now, the values should be min=5, max=11
    

1 Ответ

0 голосов
/ 02 марта 2020

Я думаю, что ваш лучший вызов будет использовать Advice, что позволит вам писать шаблоны кода, которые позже будут встроены в сгенерированный класс:

class MinMaxAdvice {
  @Advice.OnMethodExit
  static void minMax(@Min int min, @Max int max) { ... }
}

Метод реализует ваш код и может переопределять любые значения полей. Поскольку ваше разрешение зависит от имени метода, вам необходимо добавить собственные привязки для пользовательских аннотаций @Min и @Max, чтобы узнать, как это сделать, обратитесь к API Advice.withCustomBinding(). Вы можете повторно использовать привязки полей, которые уже существуют, но вы должны расположить эти поля так, как вы этого хотите.

...