ByteBuddy:如何实现字段访问拦截器?

ByteBuddy: How to implement field access interceptor?

我正在尝试制作一个 OGM 以将对象转换为 OrientDB 的 Vertex。目前我正在使用 GCLib 但我读到 ByteBuddy 可以实现两个关键的东西,如果工作,它将提高 OGM 速度。

  1. ByteBuddy可以实现字段访问控制吗?我看了文档,但不清楚或者我不明白。

  2. 动态添加默认的空构造函数。

当前的问题是:我们不知道将作为参数传递的 class 定义。这个想法是重新定义 class 并在没有构造函数的情况下实现空构造函数,添加一个名为 __BB__Dirty 的字段以在检测到分配操作时将对象设置为脏对象并强制实现接口与对象交谈。

示例: 通用 class:

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

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

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

现在我们有这样的界面:

public interface DirtyCheck {
    public boolean isDirty();
}

因此,我想强制示例 class 实现接口、方法 isDirty()、要处理的字段和默认构造函数,因此 class 应转换为:

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.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)

我卡在了解决 BB 问题的第一阶段。 谢谢

Java 虚拟机不支持在重新定义 class 时更改已加载的 class 的布局。这不是 Byte Buddy 的限制,而是 VM 实现的限制。

为了做你想做的事,你应该看看 AgentBuilder API 它允许你修改 classes before他们加载。但是,创建代理确实需要您在启动时将其显式添加为代理(与将库添加到 class 路径相反。

您可以通过调用实现接口:

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

您还可以通过简单地定义一个来添加默认构造函数:

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

后面的定义需要superclass定义默认构造函数