简体   繁体   中英

How in Java to assign to fields with Byte Buddy?

I'm having difficulty understanding the documentation for Byte Buddy . To help me learn the API I would like to generate the byte code equivalent of this Java:

    public final class GeneratedByByteBuddy {
        private final int a;
        public GeneratedByByteBuddy(final int a) {
            this.a = a;
        }
    }

I've had difficulty working out the right way to use Instrumentation to create the field assignment.

You are creating a class with customized byte code. For this, you cannot use a built-in Instrumentation but you need to write your own instrumentation which creates the particular byte code for your constructor. This Instrumentation can of course be implemented for Byte Buddy, but if you know all the specifics about your generated class, it would be better to compile this class using javac . I assume you want to learn about the API and this is not the actual class you are trying to create.

For your example class, you would need to implement the equivalent of the desugared version of the constructor. Constructors are only methods to the Java runtime but they follow specific semantics which are enforced by the JVM's verifier. The desugared constructor looks like:

public GeneratedByByteBuddy(int a) {
  super();
  this.a = a;
  return;
}

The Byte Buddy implementation of this very same class looks like this:

new ByteBuddy()
  .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
  .name("my.company.GeneratedByByteBuddy")
  .defineField("a", int.class, Visibility.PRIVATE, FieldManifestation.FINAL)
  .defineConstructor(Arrays.<Class<?>>asList(int.class), Visibility.PUBLIC)
  .intercept(new Instrumentation() {
    @Override
    public InstrumentedType prepare(InstrumentedType instrumentedType) {
      return instrumentedType;
    }

    @Override
    public ByteCodeAppender appender(final Target instrumentationTarget) {
      return new ByteCodeAppender() {
        @Override
        public boolean appendsCode() {
          return true;
        }

        @Override
        public Size apply(MethodVisitor methodVisitor,
                          Context instrumentationContext,
                          MethodDescription instrumentedMethod) {
          StackManipulation.Size size = new StackManipulation.Compound(
            MethodVariableAccess.REFERENCE.loadFromIndex(0),
            MethodInvocation.invoke(new TypeDescription.ForLoadedType(Object.class)
              .getDeclaredMethods()
              .filter(isConstructor().and(takesArguments(0))).getOnly()),
            MethodVariableAccess.REFERENCE.loadFromIndex(0),
            MethodVariableAccess.INTEGER.loadFromIndex(1),
            FieldAccess.forField(instrumentationTarget.getTypeDescription()
              .getDeclaredFields()
              .named("a"))
              .putter(),
            MethodReturn.VOID
          ).apply(methodVisitor, instrumentationContext);
          return new Size(size.getMaximalSize(), instrumentedMethod.getStackSize());
        }
      };
    }
  })
  .make()
  .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.INJECTION);

Each method implementation is implemented by an Instrumentation which is able to modify the created type by adding fields or methods. This is not necessary for your class. Then, it emits a ByteCodeAppender which is used queried for writing byte code instructions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM