ByteBuddy:新定义的字段通过反射不可见 [英] ByteBuddy: newly defined fields not visible through reflection

查看:153
本文介绍了ByteBuddy:新定义的字段通过反射不可见的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Agent 中使用 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)))

我的拦截器类看起来像这样:

公共静态类 TestRunnableConstructorInterceptor {@Advice.OnMethodExitpublic static voidintercept(@Advice.This Object thiz, @Advice.FieldValue(value="foo",readOnly=false) String foo) 抛出异常 {foo = "baz";//这样就成功设置了值}}公共静态类 TestRunnableRunInterceptor {@Advice.OnMethodEnter公共静态无效拦截(@Advice.This Object thiz,@Advice.FieldValue(foo")字符串foo)抛出异常{System.out.println(foo);//打印baz"thiz.getClass().getField("foo");//java.lang.NoSuchFieldException}}

我可以看到 ByteBuddy 正在通过 FieldValue 注释传递新定义的字段,但反射性地该变量不可见 - 可能是因为反射应用于原始类,而不是重新定位"类?

这是预期的行为吗?有没有办法通过反射访问这个新字段?

这可能与 Runnables 是 lambdas 有关系吗?我使用的是 Advice 而不是 MethodDelegation 因为如果我尝试在 Runnable#run 上使用 MethodDelegation 我会收到这样的错误(来自我的拦截侦听器)

未能转换 java.util.concurrent.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRS$auxiliary$7BgjnLbO(加载前)+异常:java.lang.IllegalStateException:无法解析 java.util.concurrent 的类型描述.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRS 无法解析 java.util.concurrent.ThreadPoolExecutor$Worker$auxiliary$8cXEOSRSnet.bytebuddy.pool.TypePool$Resolution$Illegal.resolve(TypePool.java:134) 的类型描述

解决方案

如评论中所述,您需要使用 getDeclaredField 方法而不是 getField定位非公共字段.

我假设您的 MethodDelegation 会在您检测任何 Runnable 时呈现错误.您是否在委托方法中请求 @SuperMethodCall 代理?在这种情况下,您正在指示 Byte Buddy 也检测这些类,因为它们的字节码没有持久化.

通常,Byte Buddy 从检测中排除合成类.当您检测 java.* 命名空间时,我假设您没有使用默认的排除匹配器?理想情况下,您应该限制检测类型的空间,例如通过名称,您还可以排除包含 $auxiliary$ 的类.否则,您仍然可以按默认方式排除合成类.

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)

解决方案

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.

这篇关于ByteBuddy:新定义的字段通过反射不可见的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆