Вы создаете класс с настроенным байт-кодом. Для этого вы не можете использовать встроенный Instrumentation
, но вам нужно написать свой собственный инструментарий, который создает определенный байтовый код для вашего конструктора. Этот Instrumentation
, конечно, можно реализовать и для Byte Buddy, но если вы знаете все особенности сгенерированного вами класса, лучше скомпилировать этот класс с помощью javac. Я предполагаю, что вы хотите узнать об API, и это не тот класс, который вы пытаетесь создать.
Для вашего примера класса вам нужно будет реализовать эквивалент версии конструктора без сахара. Конструкторы — это только методы среды выполнения Java, но они следуют определенной семантике, которая обеспечивается верификатором JVM. Обессахарный конструктор выглядит так:
public GeneratedByByteBuddy(int a) {
super();
this.a = a;
return;
}
Реализация Byte Buddy этого же класса выглядит так:
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);
Каждая реализация метода реализуется Instrumentation
, который может изменять созданный тип, добавляя поля или методы. Это не обязательно для вашего класса. Затем он выдает ByteCodeAppender
, который используется для записи инструкций байт-кода.
person
Rafael Winterhalter
schedule
14.07.2014