ByteBuddy: newly defined fields not visible through reflection

∥☆過路亽.° 提交于 2019-12-11 02:05:53

问题


I use ByteBuddy in an Agent to add a tracking variable to each Runnable in a test program:

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))
            )

With my Interceptor classes looking like this:

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
    }
}

I can see that ByteBuddy is passing through the newly defined field via the FieldValue annotation, but reflectively the variable is not visible - perhaps because the reflection is being applied to the original class, and not the 'rebased' class?

Is this the expected behavior? Is there a way to access this new field via reflection?

Could this be something to do with the Runnables being lambdas? I'm using Advice rather than MethodDelegation because if I try to use MethodDelegation on Runnable#run I get errors like this (from my interception Listener)

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:


As mentioned in the comments, you need to use the getDeclaredField method instead of getField when you want to locate non-public fields.

I assume that your MethodDelegation renders errors as you instrument any Runnable. Do you request @SuperMethodCall proxies in your delegation method? In this case, you are instructing Byte Buddy to also instrument these classes what is impossible as their byte code is not persisted.

Normally, Byte Buddy excludes synthetic classes from instrumentation. As you instrument the java.* namespace, I assume that you are not using the default exclusion matcher? You should ideally restrain your space of instrumented types, for example by name where you could also exclude classes containing $auxiliary$. Otherwise, you can still exclude synthetic classes as it is by default.



来源:https://stackoverflow.com/questions/44055343/bytebuddy-newly-defined-fields-not-visible-through-reflection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!