使用byte-buddy-agent修改java.util类 [英] Modify java.util class using byte-buddy-agent
问题描述
是否可以使用字节伙伴在java.util类中添加字段?
Is it possible to add a field in java.util class using byte-buddy?
我试图在java.util.concurrent.FutureTask中添加一个字段,并拦截构造函数和一个任意方法来设置和获取字段值.简而言之,我正在尝试向FutureTask添加一个字段,以便即使它们在线程池中运行,也可以将某些值从父级传递给子线程.无法在FutureTask中添加字段?
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
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
GetPitAdvice
@Advice.OnMethodEnter
public static void getValues(@Advice.FieldValue(value = "pit") String pit)
throws Exception {
logger.info("pit in future Task {}", pit);
}
SetPitAdvice
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
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显示类已转换
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.
推荐答案
如果该类已经加载,则不可能.相反,您可以使用 Instrumentation
API将类注入到引导类加载器中,并在此类中存储一个带有弱键的静态映射,您可以在其中放置每个实例的字段值.
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.
另外,请注意, until 类是由引导类加载器加载的,而引导类加载器看不到代理所包含的由系统类加载器加载的任何类.因此,建议使用的所有API都必须注入到引导加载程序中.
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.
这篇关于使用byte-buddy-agent修改java.util类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!