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$, В противном случае вы все равно можете исключить синтетические классы, так как это по умолчанию.

Другие вопросы по тегам