问题
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