ByteBuddy: как выполнить делегирование / пересылку метода в изменчивое поле

#java #byte-buddy

#java #byte-buddy

Вопрос:

У меня есть (псевдокод) что-то вроде этого:

 final Class<OUT> newClass = (Class<OUT>) new ByteBuddy()
.subclass(Object.class)
.name(newName)
.implement(SomeInterface.class, SomeOtherInterface.class)
.method(ElementMatchers.isMethod())                            
.intercept(
    ExceptionMethod.throwing(UnsupportedOperationException.class,
                            "calling this method is not supported"))
// in fact I am matching for more than a single method here
.method(ElementMatchers.named("getUuid"))
.intercept(
    MethodDelegation.toInstanceField(SomeOtherInterface.class, "delegate"))
.make()
.load(aBusinessEntityClass.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
  

Моя текущая проблема: мне нужно, чтобы мое поле делегата было изменчивым. Как я могу этого добиться?

Комментарии:

1. К сожалению, предложенное вами решение является самым простым. Я постараюсь сделать API немного более гибким в будущей версии: github.com/raphw/byte-buddy/issues/202

2. @RafaelWinterhalter: Спасибо за ваш комментарий и добавление запроса на улучшение.

Ответ №1:

toInstanceField API был удален с Byte Buddy 1.5.0 в пользу нового инструментария API, где вы скорее определяете поле явно:

 new ByteBuddy()
  .defineField("delegate", SomeOtherInterface.class, VOLATILE)
  .method(ElementMatchers.named("getUuid"))
  .intercept(MethodDelegation.toField("delegate"));
  

Это позволяет выполнять другие действия, такие как добавление аннотаций и т.д.

Этот подход теперь реализован для всех Implementation ов. Сегодня была выпущена новая версия.

Комментарии:

1. О, это хорошее решение моей проблемы. В будущих проектах я, возможно, рассмотрю это! 🙂

Ответ №2:

Хорошо, нашел решение, которое работает для меня, на случай, если кому-то интересно:

 final Class<OUT> newClass = (Class<OUT>) new ByteBuddy()
.subclass(Object.class)
.name(newName)
.implement(SomeInterface.class, SomeOtherInterface.class)
.method(ElementMatchers.isMethod())                            
.intercept(
    ExceptionMethod.throwing(UnsupportedOperationException.class,
                        "calling this method is not supported"))
// in fact I am matching for more than a single method here
.method(ElementMatchers.named("getUuid"))
.intercept(
    MethodDelegation.toInstanceField(SomeOtherInterface.class, "delegate"))
// -- HERE THE FIELD IS MODIFIED AGAIN, IN THIS CASE AS  --
// -- PRIVATE amp; VOLATILE                                 --
.field(ElementMatchers.named("delegate"))                       
.transform(Transformer.ForField.withModifiers(FieldManifestation.VOLATILE, Visibility.PRIVATE))
.make()
.load(aBusinessEntityClass.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
  

Решение состояло в том, чтобы впоследствии изменить поле с помощью вызова field() с помощью transform() и соответствующего Transformer.ForField.withModifiers().

Надеюсь помочь всем, кто сталкивается с этой проблемой.