使用 byte-buddy-agent 修改 java.util class
Modify java.util class using byte-buddy-agent
是否可以使用 byte-buddy 在 java.util class 中添加一个字段?
我正在尝试在 java.util.concurrent.FutureTask 中添加一个字段并拦截构造函数和任意方法来设置和获取字段值。简而言之,我正在尝试向 FutureTask 添加一个字段,以便我可以将一些值从父线程传递给子线程,即使它们在线程池中 运行 也是如此。不能在 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 显示 class 已转换
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
但是 none 我的建议拦截被调用了。
如果 class 已经加载,这是不可能的。相反,您可以使用 Instrumentation
API 将 class 注入 bootstrap class 加载器,并在该 [=22] 中存储一个带有弱键的静态映射=] 放置每个实例的字段值的位置。
此外,请注意 until classes 由 bootstrap class 加载程序加载,无法看到任何 classes 包含在你的代理中,由系统 class 加载器加载。出于这个原因,建议使用的任何 API 都必须注入到 bootstrap 加载程序中。
是否可以使用 byte-buddy 在 java.util class 中添加一个字段?
我正在尝试在 java.util.concurrent.FutureTask 中添加一个字段并拦截构造函数和任意方法来设置和获取字段值。简而言之,我正在尝试向 FutureTask 添加一个字段,以便我可以将一些值从父线程传递给子线程,即使它们在线程池中 运行 也是如此。不能在 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 显示 class 已转换
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
但是 none 我的建议拦截被调用了。
如果 class 已经加载,这是不可能的。相反,您可以使用 Instrumentation
API 将 class 注入 bootstrap class 加载器,并在该 [=22] 中存储一个带有弱键的静态映射=] 放置每个实例的字段值的位置。
此外,请注意 until classes 由 bootstrap class 加载程序加载,无法看到任何 classes 包含在你的代理中,由系统 class 加载器加载。出于这个原因,建议使用的任何 API 都必须注入到 bootstrap 加载程序中。