ByteBuddy: как реализовать перехватчик доступа к полю?

Я пытаюсь сделать OGM для перевода объекта в Vertex для OrientDB. В настоящее время я использую GCLib, но я читал, что ByteBuddy может реализовать две важные вещи, которые, если они будут работать, увеличат скорость OGM.

  1. Может ли ByteBuddy реализовать контроль доступа к полям? Я прочитал документ, но он не ясен или я его не понимаю.

  2. Динамически добавить пустой конструктор по умолчанию.

Текущая проблема заключается в следующем: мы не знаем определение класса, которое будет передано в качестве параметра. Идея состоит в том, чтобы переопределить класс и реализовать пустой конструктор, если его нет, добавить поле с именем __BB__Dirty, чтобы установить объект как грязный, если была обнаружена операция назначения, и заставить реализацию интерфейса общаться с объектом.

Пример: общий класс:

public class Example {
   int i = 0;
   String stringField;

   public Example(Strinf s) {
       stringField = s;
   }

   public void addToI(){
       i++;
   }
}

Теперь у нас есть такой интерфейс:

public interface DirtyCheck {
    public boolean isDirty();
}

Итак, я хочу заставить класс Example реализовать интерфейс, метод isDirty(), поле для работы и конструктор по умолчанию, поэтому класс должен быть переведен в:

public class Example implements DirtyCheck {
   int i = 0;
   String stringField;

   boolean __BB__dirty = false;

   public Example() {

   }

   public Example(Strinf s) {
       stringField = s;
   }

   public void addToI(){
       i++;
   }

   public boolean isDirty() {
       return this.__BB__dirty;
   }
}

и какой-то волшебный присваиватель, поэтому, если какое-либо поле (кроме __BB__dirty) изменяется, для поля __BB__dirty устанавливается значение True;

Я пробовал первую часть этого, но у меня не получилось :(

...
ByteBuddyAgent.install();

Example ex = new ByteBuddy()
                .redefine(Example.class)
                .defineField("__BB__Dirty", boolean.class, Visibility.PUBLIC)
                .make()
                .load(Example.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent())
                .getLoaded().newInstance();
 ....

 ex.addToI();    // <--- this should set __BB__dirty to true since it
                 //      assign a value to i.

Но я получаю эту ошибку:

Exception in thread "main" java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy$1.apply(ClassReloadingStrategy.java:297)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:173)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:4350)
at Test.TestBB.<init>(TestBB.java:33)
at Test.TestBB.main(TestBB.java:23)

Застрял на самом первом этапе решения проблемы с ББ. Спасибо


person Marcelo D. Ré    schedule 26.04.2016    source источник


Ответы (1)


Виртуальная машина Java не поддерживает изменение макета уже загруженных классов при переопределении класса. Это не ограничение Byte Buddy, а реализация виртуальной машины.

Чтобы сделать то, что вы хотите, вы должны взглянуть на AgentBuilder API, который позволяет вам изменять классы до их загрузки. Однако создание агента требует, чтобы вы добавили его явно в качестве агента при запуске (в отличие от добавления библиотеки в путь к классу.

Вы можете реализовать интерфейс, вызвав:

.implement(DirtyCheck.class).intercept(FieldAccessor.of("__dirty__");

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

.defineConstructor(Visibility.PUBLIC).intercept(SuperMethodCall.INSTANCE)

Последнее определение требует, чтобы суперкласс определял конструктор по умолчанию.

person Rafael Winterhalter    schedule 27.04.2016
comment
Спасибо за ответ. Javadoc фактически говорит, что на данный момент не разрешено добавлять поле или метод, и эта функция будет добавлена ​​​​в будущей версии. Я полагаю, что мне нужно найти другой путь. Я закончу OGM с GCLib и в ближайшее время вернусь, чтобы посмотреть, смогу ли я реализовать его в ББ в понятной форме. - person Marcelo D. Ré; 27.04.2016
comment
Пожалуйста. Обратите внимание, что вы можете делать все, что возможно с cglib, также с Byte Buddy, только Byte Buddy предлагает лучшую производительность во время выполнения. - person Rafael Winterhalter; 27.04.2016