问题
Is it possible to add a field in java.util class using byte-buddy?
I am trying to add a field in java.util.concurrent.FutureTask and intercept constructor and an arbitrary method to set and get the field value. In short, I am trying to add a field to FutureTask so that I can pass some value to child thread from parent even if they are running in a thread pool. isn't it possible to add a field in FutureTask?
FutureTaskTransofrmer
@Override
protected ElementMatcher.Junction<TypeDescription> getNarrowTypesMatcher() {
return named("java.util.concurrent.FutureTask");
}
@Override
public AgentBuilder.Transformer getTransformer() {
return (builder, typeDescription, classLoader, module) -> {
beforeTransformation(typeDescription, classLoader);
return builder
.defineField("pit", String.class)
.constructor(ElementMatchers.any())
.intercept(Advice.to(SetPitAdvice.class))
.method(named("run"))
.intercept(Advice.to(GetPitAdvice.class))
;
};
}
GetPitAdvice
@Advice.OnMethodEnter
public static void getValues(@Advice.FieldValue(value = "pit") String pit)
throws Exception {
logger.info("pit in future Task {}", pit);
}
SetPitAdvice
@Advice.OnMethodExit
public static void setValues(
@Advice.FieldValue(value = "pit", readOnly = false) String pit
)
throws Exception {
logger.debug("Setting pit field in FutureTask");
pit = SimulationRequestContextHolder.get("pit");
}
AgentBuilder
private static AgentBuilder createAgentBuilder(AutoEvictingCachingBinaryLocator binaryLocator) {
final ByteBuddy byteBuddy = new ByteBuddy()
.with(TypeValidation.of(AgentProperties.PROPERTIES.isDebugInstrumentation()))
.with(MethodGraph.Compiler.ForDeclaredMethods.INSTANCE);
return new AgentBuilder.Default(byteBuddy)
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(getListener())
.with(binaryLocator)
.ignore(any(), ClassLoaderNameMatcher.isReflectionClassLoader())
.or(any(), ClassLoaderNameMatcher.classLoaderWithName("org.codehaus.groovy.runtime.callsite.CallSiteClassLoader"))
.or(any(), new IsIgnoredClassLoaderElementMatcher())
.or(nameStartsWith("org.aspectj.")
.or(nameStartsWith("org.groovy."))
.or(nameStartsWith("com.sun."))
.or(nameStartsWith("com.p6spy."))
.or(nameStartsWith("net.bytebuddy."))
.or(nameStartsWith("org.slf4j.").and(not(nameStartsWith("org.slf4j.impl."))))
.or(nameContains("javassist"))
.or(nameContains(".asm."))
.or(nameStartsWith("com.java.agent.sims")
))
// .disableClassFormatChanges()
.enableUnsafeBootstrapInjection()
;
}
onTransformListener shows the class is transformed
11:27:24.141 [main] INFO com.java.agent.sims.ApplicationClassLoaderMatcher - Instrumenting ClassLoader null: true
11:27:24.186 [main] DEBUG com.java.agent.sims.transformers.ByteBuddyTransformer - TRANSFORM java.util.concurrent.FutureTask (FutureTaskTransformer)
11:27:24.466 [main] INFO com.java.agent.sims.instrument.TransformListener - Class modified by Byte Buddy: java.util.concurrent.FutureTask
but none of my advice intercepts are getting called.
回答1:
If the class is already loaded, this is not possible. Instead, you can inject a class into the bootstrap class loader using the Instrumentation
API and have a static map with weak keys stored in this class where you place the field value of each instance.
Also, note that the until classes are loaded by the bootstrap class loader that cannot see any classes contained by your agent which is loaded by the system class loader. Any API used by advice must be injected into the bootstrap loader for this reason.
来源:https://stackoverflow.com/questions/56641621/modify-java-util-class-using-byte-buddy-agent