ByteBuddy: новые определенные поля не видны через отражение
Я использую ByteBuddy в агенте, чтобы добавить переменную отслеживания для каждого Runnable в тестовой программе:
new AgentBuilder.Default()
.with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED)
.type(ElementMatchers.isSubTypeOf(Runnable.class)
.and(ElementMatchers.not(ElementMatchers.isInterface())))
.and(ElementMatchers.not(ElementMatchers.isAbstract()))
.transform((builder, typeDescription, classLoader, module) -> builder
.defineField("foo", String.class)
.constructor(ElementMatchers.any())
.intercept(Advice.to(TestRunnableConstructorInterceptor.class))
.method(ElementMatchers.named("run"))
.intercept(Advice.to(TestRunnableRunInterceptor.class))
)
Мои классы перехватчиков выглядят так:
public static class TestRunnableConstructorInterceptor {
@Advice.OnMethodExit
public static void intercept(@Advice.This Object thiz, @Advice.FieldValue(value="foo",readOnly=false) String foo) throws Exception {
foo = "baz"; // this sets the value successfully
}
}
public static class TestRunnableRunInterceptor {
@Advice.OnMethodEnter
public static void intercept(@Advice.This Object thiz, @Advice.FieldValue("foo") String foo) throws Exception {
System.out.println(foo); //prints "baz"
thiz.getClass().getField("foo"); // java.lang.NoSuchFieldException
}
}
Я вижу, что ByteBuddy проходит через вновь определенное поле с помощью аннотации FieldValue, но отражательно переменная не видна - возможно, потому что отражение применяется к исходному классу, а не к классу "rebased"?
Это ожидаемое поведение? Есть ли способ получить доступ к этому новому полю через отражение?
Может быть это как-то связано с тем, что Runnables - это лямбды? Я использую Advice, а не MethodDelegation, потому что, если я пытаюсь использовать MethodDelegation в Runnable #run, я получаю такие ошибки (из моего прослушивателя перехвата)
Failed to transform java.util.concurrent.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRS$auxiliary$7BgjnLbO (before loading) + Exception: java.lang.IllegalStateException: Cannot resolve type description for java.util.concurrent.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRS Cannot resolve type description for java.util.concurrent.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRSnet.bytebuddy.pool.TypePool$Resolution$Illegal.resolve(TypePool.java:134)
1 ответ
Как уже упоминалось в комментариях, вы должны использовать getDeclaredField
метод вместо getField
когда вы хотите найти непубличные поля.
Я полагаю, что ваш MethodDelegation
выдает ошибки, как вы инструмент любой Runnable
, Вы запрашиваете @SuperMethodCall
прокси в вашем методе делегирования? В этом случае вы инструктируете Byte Buddy также инструктировать эти классы, что невозможно, поскольку их байтовый код не сохраняется.
Обычно Byte Buddy исключает синтетические классы из инструментовки. Как вы инструмент java.*
namespace, я предполагаю, что вы не используете сопоставление исключения по умолчанию? В идеале вы должны ограничить пространство инструментальных типов, например, по имени, где вы также можете исключить классы, содержащие $auxiliary$
, В противном случае вы все равно можете исключить синтетические классы, так как это по умолчанию.