Search code examples
javabytecodebyte-buddy

How to add a field to a class in ByteBuddy and set / get that value in a method interceptor


I am using byte-buddy to build an ORM on top of Ignite, we need to add a field to a class and then access it in a method interceptor..

So here's an example where I add a field to a class

final ByteBuddy buddy = new ByteBuddy();

final Class<? extends TestDataEnh> clz =  buddy.subclass(TestDataEnh.class)
        .defineField("stringVal",String.class)
        .method(named("setFieldVal")).intercept(
            MethodDelegation.to(new SetterInterceptor())
    )
    .make()
    .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
    .getLoaded();

final TestDataEnh enh = clz.newInstance();

enh.getFieldVal();
enh.setFieldVal();

System.out.println(enh.getClass().getName());

And the Interceptor is like this

public class SetterInterceptor {
    @RuntimeType
    public  Object intercept() {
        System.out.println("Invoked method with: ");
        return null;
    }
}

So how do I get the value of the new field into the interceptor so I can change it's value? (stringVal)

Thanks in advance


Solution

  • You can use a FieldProxy to access a field by its name. You need to install a FieldProxy.Binder and register it on the MethodDdelegation before you can use it as it requires a custom type for type-safe instrumentation. The javadoc explains how this can be done. Alternatively, you can use reflection on an instance by using @This. The JVM is quite efficient in optimizing the use of reflection.

    An example would be:

    interface FieldGetter {
      Object getValue();
    }
    
    interface FieldSetter {
      void setValue(Object value);
    }
    
    public class SetterInterceptor {
      @RuntimeType
      public  Object intercept(@FieldProxy("stringVal") FieldGetter accessor) {
        Object value = accessor.getValue();
        System.out.println("Invoked method with: " + value);
        return value;
      }
    }
    

    For bean properties, the FieldProxy annotation does not require an explicit name but discovers the name from the name of the intercepted getter or setter.

    The installation can be done as follows:

    MethodDelegation.to(SetterInterceptor.class)
                    .appendParameterBinder(FieldProxy.Binder.install(FieldGetter.class, 
                                                                     FieldSetter.class));