3

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

Holger
  • 285,553
  • 42
  • 434
  • 765
Kevin Daly
  • 93
  • 1
  • 7

1 Answers1

1

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));
Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • Ok in my case we don't know what the name of the field will be until Runtime, but I think you've given me enough to work on .. We are adding a hidden field to store some relational information in the class before serializing it to the data store.. – Kevin Daly Feb 01 '16 at 13:43
  • Just for future reference of this Stack Overflow.. How would I access a field if I don't know the name of the field until runtime? I'm looking at FieldProxy.Binder.install(TypeDescription.AbstractBase.OfSimpleType To put the field name in, but I get lost there. – Kevin Daly Feb 01 '16 at 14:16
  • Do the field names follow the Java bean convention? The simply leave the field empty. Otherwise, you can really just use reflection on a `@This` object. As long as you cache the `Field` instances, this has no relevance to performance. Access of reflection is achieved by reading directly from the heap using `sun.misc.Unsafe`. No byte code generation can compete with this level of performance. – Rafael Winterhalter Feb 01 '16 at 14:30