如何应用、删除和重新应用 bytebuddy 转换?
How to apply, remove and re-apply the bytebuddy transformation?
我正尝试通过 javaagent apply, remove and re-apply
bytebuddy 转换作为以下代码。删除通过 ResettableClassFileTransformer::reset
正常工作,但重新应用无效。
// The target classes
public @interface ToString {
}
public interface Greetable {
String say(final String name);
}
@ToString
public class Greeter implements Greetable {
public String say(final String name) {
return "Hello " + name;
}
}
//note the some transform is from the example
new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classloader,
JavaModule module) {
return builder.method(named("say"))
.intercept(FixedValue.value("transformed");
}
})
// The transformation
public class MyProgram {
public static void main(final String[] args) {
ByteBuddyAgent.install();
AgentBuilder bldr = new AgentBuilder.Default().
with(AgentBuilder.Listener.
StreamWriting.
toSystemOut()).
with(RedefinitionStrategy.
RETRANSFORMATION).
disableClassFormatChanges().
type(isSubTypeOf(
Greetable.class)).
transform(<some transform>);
ResettableClassFileTransformer resetter =
bldr.installOnByteBuddyAgent();
Greetable g1 = new Greeter();
System.out.println(g1.say("001"));
//remove the transformation
resetter.reset(ByteBuddyAgent.getInstrumentation(),
RedefinitionStrategy.RETRANSFORMATION);
Greetable g2 = new Greeter();
System.out.println(g2.say("002"));
//re-apply the transformation
bldr.installOn(ByteBuddyAgent.getInstrumentation());
Greetable g3 = new Greeter();
System.out.println(g3.say("003"));
}
}
输出为
TRANSFORM Greetable [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
TRANSFORM Greeter [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
transformed //note: the g1.say() is transformed.
Hello 002 //note: the g2 is an orginal
TRANSFORM Greetable [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
TRANSFORM Greeter [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
Hello 003 //note the re-apply has not worked.
能否请您帮忙指教如何实现重新申请?
编辑1
我已将 byte-buddy
更新到版本 1.6.11
并更改了 AgentBuilder.Transformer
。结果还是一样。 g3.say()
未转换。
我的环境是
Windows 10 64Bits
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
编辑2
我已经从界面更改了类型匹配器
type(isSubTypeOf(Greetable.class))
具体class为
type(isSubTypeOf(Greeter.class))
将g3.say()
转化为属性。接口匹配转换有没有限制?
我从 AgentBuilder.Transformer
API 可以看出,您是 运行 老版本的 Byte Buddy。我刚刚尝试使用最新版本并且它有效。
实际上,您是否依赖 @ToString
注释来触发转换?当您转换尚未加载的 class 时,字节好友会直接从 class 文件中读取注释。对于加载的 class,它使用加载的表示。您的注释未使用 @RetentionPolicy(Retention.RUNTIME)
进行注释以保留可能存在问题的注释可见性。
更新:您需要通过 not(isAbstract()).and(named("say"))
从接口中排除 say
方法来优化您的方法匹配器。否则,您将在重新转换尝试中包含该接口。 JVM 似乎因此而默默地失败了第二次重新转换,因为在加载 class 之后您无法将抽象方法更改为默认方法。通常,这会触发异常,但在您的情况下,VM 似乎会默默地抑制错误。
我正尝试通过 javaagent apply, remove and re-apply
bytebuddy 转换作为以下代码。删除通过 ResettableClassFileTransformer::reset
正常工作,但重新应用无效。
// The target classes
public @interface ToString {
}
public interface Greetable {
String say(final String name);
}
@ToString
public class Greeter implements Greetable {
public String say(final String name) {
return "Hello " + name;
}
}
//note the some transform is from the example
new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder transform(DynamicType.Builder builder,
TypeDescription typeDescription,
ClassLoader classloader,
JavaModule module) {
return builder.method(named("say"))
.intercept(FixedValue.value("transformed");
}
})
// The transformation
public class MyProgram {
public static void main(final String[] args) {
ByteBuddyAgent.install();
AgentBuilder bldr = new AgentBuilder.Default().
with(AgentBuilder.Listener.
StreamWriting.
toSystemOut()).
with(RedefinitionStrategy.
RETRANSFORMATION).
disableClassFormatChanges().
type(isSubTypeOf(
Greetable.class)).
transform(<some transform>);
ResettableClassFileTransformer resetter =
bldr.installOnByteBuddyAgent();
Greetable g1 = new Greeter();
System.out.println(g1.say("001"));
//remove the transformation
resetter.reset(ByteBuddyAgent.getInstrumentation(),
RedefinitionStrategy.RETRANSFORMATION);
Greetable g2 = new Greeter();
System.out.println(g2.say("002"));
//re-apply the transformation
bldr.installOn(ByteBuddyAgent.getInstrumentation());
Greetable g3 = new Greeter();
System.out.println(g3.say("003"));
}
}
输出为
TRANSFORM Greetable [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
TRANSFORM Greeter [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
transformed //note: the g1.say() is transformed.
Hello 002 //note: the g2 is an orginal
TRANSFORM Greetable [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
TRANSFORM Greeter [sun.misc.Launcher$AppClassLoader@7ab2bfe1,
null,
loaded=true]
Hello 003 //note the re-apply has not worked.
能否请您帮忙指教如何实现重新申请?
编辑1
我已将 byte-buddy
更新到版本 1.6.11
并更改了 AgentBuilder.Transformer
。结果还是一样。 g3.say()
未转换。
我的环境是
Windows 10 64Bits
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)
编辑2
我已经从界面更改了类型匹配器
type(isSubTypeOf(Greetable.class))
具体class为
type(isSubTypeOf(Greeter.class))
将g3.say()
转化为属性。接口匹配转换有没有限制?
我从 AgentBuilder.Transformer
API 可以看出,您是 运行 老版本的 Byte Buddy。我刚刚尝试使用最新版本并且它有效。
实际上,您是否依赖 @ToString
注释来触发转换?当您转换尚未加载的 class 时,字节好友会直接从 class 文件中读取注释。对于加载的 class,它使用加载的表示。您的注释未使用 @RetentionPolicy(Retention.RUNTIME)
进行注释以保留可能存在问题的注释可见性。
更新:您需要通过 not(isAbstract()).and(named("say"))
从接口中排除 say
方法来优化您的方法匹配器。否则,您将在重新转换尝试中包含该接口。 JVM 似乎因此而默默地失败了第二次重新转换,因为在加载 class 之后您无法将抽象方法更改为默认方法。通常,这会触发异常,但在您的情况下,VM 似乎会默默地抑制错误。