3

I was trying to find usable answer but with no luck. I wonder if it would be possible to use byte-buddy for following: Let's say we have a POJO with number of values. For some specific kind of processing i am interested only in some. I could mark them with an annotation, like @ConditionalData i could put on getters or fields. Then I could create and inteface, lets say NVPProvider which would return map field name - value. Doing this with reflection is possible but it is not too performant. I Was hoping i could use byte buddy to extend the class by the interface and implement method, but i am not really finding how to construct the Implementation to achieve that.

I have went through implementations of:

net.bytebuddy.implementation.Implementation

and tried to search for some examples around web pages, but I haven't recognized the right way to go with.

public interface NVPProvider {
    Map<String, Object> getDataAsNVP();
}

public <O> Builder<O> instrumentType(Builder<O> builder) {
    builder.implement(NVPProvider.class).method(net.bytebuddy.matcher.ElementMatchers.named("getDataAsNVP")).intercept( ??? );        
    return builder.implement(NVPProvider.class);
}

I wonder if there is a way that i would iterate the fields and getters and match the Accessible objects by reflectively getting the annotations, however based on this i would be able to compose the interface method implementation iterating the matched fields and contributing to the result map, the imaginary generated code would look like:

Map<String, Object> result = new HashMap<>();
Object obj0 = getValue001();
result.put("getValue001", obj0);

Object ob10 = accesibleField1;
result.put("ob10 ", ob10 );

Later i could add annotation attribute for better looking keys.

I have seen examples with

MethodDelegation.to(interceptor)

however, then I do now see how to do it without reflection.

I have idea how to so it with Javassist, where you can actually can compose pieces of code you later compile, but I am not sure how to do it with byte-buddy. I have used byte-budy for extension of POJO by simple getters dynamically defined in config file and it looks nice. Having just one byte-manipulation tool would be cleaner. Thanks for any advices.

1 Answers1

1

This is possible in Byte Buddy but you would have to manufacture a custom method body by implementing your own Implementation or its underlying ByteCodeAppender. You could use ASM directly as such visitors expose their MethodVisitor but a bit easier, you could also use the higher-level constructs offered in the net.bytebuddy.implementation.bytecode package. For a method as you describe it, you would first intantiate the hash map:

List<StackManipulation> m = new ArrayList<>();

// new HashMap<>();
m.add(TypeCreation.of(HashMap.class));
m.add(Duplication.SINGLE);
m.add(MethodInvocation.invoke(new MethodDescription.ForLoadedConstructor(HashMap.class.getConstructor());

Subsequently, a ByteCodeAppender offers a description of the instrumented type from which you can navigate to all fields and methods such as:

FieldDescription field = ...

// map.put(fieldName, fieldValue);
m.add(Duplication.SINGLE);
m.add(new TextConstant(field.getName());
m.add(MethodVariableAccess.loadThis());
m.add(FieldAccess.forField(field).read());
m.add(MethodInvocation.invoke(Map.class.getMethod("put", Object.class, Object.class)));
m.add(Removal.SINGLE);

The above code first duplicates the hash map on the stack to retain it for further access, then adds the field name onto the stack, loads the fields value and then invokes the Map::put method. Finally, it drops the put method's return value.

Finally, you need to return the map by adding MethodReturn.REFERENCE.

You can have a look at other Implementations in Byte Buddy to see how this works in detail and ASM provides a good tutorial into byte code fundamentals to explain the stack metaphor that the byte code patterns need to follow.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192