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

时间:2016-04-26 18:18:35

标签: java byte-buddy

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

  1. ByteBuddy可以实现字段访问控制吗?我阅读了文档,但不清楚,或者我不理解。

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

  3. 目前的问题是:我们不知道将作为参数传递的类定义。我们的想法是重新定义类并实现空构造函数(如果没有),如果检测到assign操作,则添加一个名为__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)
    

    我陷入了解决BB问题的第一阶段。 感谢

1 个答案:

答案 0 :(得分:0)

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

为了做你想做的事,你应该看一下AgentBuilder API,它允许你在加载之前修改类。但是,创建代理需要在启动时将其显式添加为代理(而不是将库添加到类路径中。

您可以通过调用:

来实现该接口
.implement(DirtyCheck.class).intercept(FieldAccessor.of("__dirty__");

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

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

后一个定义要求超类定义默认构造函数。