使用 Byte Buddy 拦截 Object.class toString() 方法
Intercepting Object.class toString() method with Byte Buddy
我正在使用 Byte Buddy 来拦截一些 JDK 方法,System.class 和 Thread.class 很好,但不适用于 java.lang.Object。我是 运行 我在 JDK8 上的测试,它没有抛出任何错误。
final public class AgentBootstrap {
public static void premain(String agentArgs, Instrumentation inst) throws Exception {
try {
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REBASE)
.enableNativeMethodPrefix("$$mynative_")
.ignore(ElementMatchers.none())
.with(
new AgentBuilder.Listener.Filtering(
new StringMatcher("java.lang.Object", StringMatcher.Mode.EQUALS_FULLY),
AgentBuilder.Listener.StreamWriting.toSystemOut()))
.type(ElementMatchers.is(Object.class))
.transform(new Transformer() {
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
return builder.method(ElementMatchers.named("toString")).intercept(FixedValue.value("HELLO BYTE BUDDY!"));
}
})
.installOn(inst);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
并且我尝试使用Javassist,改造java.lang.Object的方法是成功的。
谁能知道为什么它在 Object.class 上不起作用?
您想使用
disableClassFormatChanges()
为了避免结构性 class 文件更改违反重新转换规则,
- 建议 API 以便将代码插入现有方法而不添加新方法。
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import java.lang.instrument.Instrumentation;
import static net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.RETRANSFORMATION;
import static net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC;
import static net.bytebuddy.matcher.ElementMatchers.*;
class Scratch {
public static class ToStringAdvice {
@Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class)
public static boolean before() {
// Skip original method execution (false is the default value for boolean)
return false;
}
@Advice.OnMethodExit
public static void after(@Advice.Return(readOnly = false, typing = DYNAMIC) Object returnValue) {
// Set fixed return value
returnValue = "HELLO BYTE BUDDY!";
}
}
public static void premain(String agentArgs, Instrumentation inst) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RETRANSFORMATION)
.with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
.with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
.with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
.ignore(none())
.type(is(Object.class))
.transform((builder, typeDescription, classLoader, module) ->
builder.visit(
Advice
.to(ToStringAdvice.class)
.on(named("toString"))
)
)
.installOn(inst);
}
public static void main(String[] args) throws Exception {
Instrumentation instrumentation = ByteBuddyAgent.install();
premain("", instrumentation);
instrumentation.retransformClasses(Object.class);
System.out.println(new Object());
}
}
这行得通,...
[Byte Buddy] BEFORE_INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@3bfdc050 on sun.instrument.InstrumentationImpl@1bce4f0a
[Byte Buddy] REDEFINE BATCH #0 [1 of 1 type(s)]
[Byte Buddy] TRANSFORM java.lang.Object [null, null, loaded=true]
[Byte Buddy] REDEFINE COMPLETE 1 batch(es) containing 1 types [0 failed batch(es)]
[Byte Buddy] INSTALL HELLO BYTE BUDDY! on HELLO BYTE BUDDY!
[Byte Buddy] TRANSFORM java.lang.Object [null, null, loaded=true]
HELLO BYTE BUDDY!
...但我认为您在操纵 JDK 核心 classes 之前应该三思。看上面的日志。你有没有注意到日志行
[Byte Buddy] INSTALL HELLO BYTE BUDDY! on HELLO BYTE BUDDY!
正在打印两个明显使用您刚才建议的方法的对象?所以要小心!
我正在使用 Byte Buddy 来拦截一些 JDK 方法,System.class 和 Thread.class 很好,但不适用于 java.lang.Object。我是 运行 我在 JDK8 上的测试,它没有抛出任何错误。
final public class AgentBootstrap {
public static void premain(String agentArgs, Instrumentation inst) throws Exception {
try {
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REBASE)
.enableNativeMethodPrefix("$$mynative_")
.ignore(ElementMatchers.none())
.with(
new AgentBuilder.Listener.Filtering(
new StringMatcher("java.lang.Object", StringMatcher.Mode.EQUALS_FULLY),
AgentBuilder.Listener.StreamWriting.toSystemOut()))
.type(ElementMatchers.is(Object.class))
.transform(new Transformer() {
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
return builder.method(ElementMatchers.named("toString")).intercept(FixedValue.value("HELLO BYTE BUDDY!"));
}
})
.installOn(inst);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
并且我尝试使用Javassist,改造java.lang.Object的方法是成功的。 谁能知道为什么它在 Object.class 上不起作用?
您想使用
disableClassFormatChanges()
为了避免结构性 class 文件更改违反重新转换规则,- 建议 API 以便将代码插入现有方法而不添加新方法。
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import java.lang.instrument.Instrumentation;
import static net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.RETRANSFORMATION;
import static net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC;
import static net.bytebuddy.matcher.ElementMatchers.*;
class Scratch {
public static class ToStringAdvice {
@Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class)
public static boolean before() {
// Skip original method execution (false is the default value for boolean)
return false;
}
@Advice.OnMethodExit
public static void after(@Advice.Return(readOnly = false, typing = DYNAMIC) Object returnValue) {
// Set fixed return value
returnValue = "HELLO BYTE BUDDY!";
}
}
public static void premain(String agentArgs, Instrumentation inst) {
new AgentBuilder.Default()
.disableClassFormatChanges()
.with(RETRANSFORMATION)
.with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
.with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
.with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
.ignore(none())
.type(is(Object.class))
.transform((builder, typeDescription, classLoader, module) ->
builder.visit(
Advice
.to(ToStringAdvice.class)
.on(named("toString"))
)
)
.installOn(inst);
}
public static void main(String[] args) throws Exception {
Instrumentation instrumentation = ByteBuddyAgent.install();
premain("", instrumentation);
instrumentation.retransformClasses(Object.class);
System.out.println(new Object());
}
}
这行得通,...
[Byte Buddy] BEFORE_INSTALL net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer@3bfdc050 on sun.instrument.InstrumentationImpl@1bce4f0a
[Byte Buddy] REDEFINE BATCH #0 [1 of 1 type(s)]
[Byte Buddy] TRANSFORM java.lang.Object [null, null, loaded=true]
[Byte Buddy] REDEFINE COMPLETE 1 batch(es) containing 1 types [0 failed batch(es)]
[Byte Buddy] INSTALL HELLO BYTE BUDDY! on HELLO BYTE BUDDY!
[Byte Buddy] TRANSFORM java.lang.Object [null, null, loaded=true]
HELLO BYTE BUDDY!
...但我认为您在操纵 JDK 核心 classes 之前应该三思。看上面的日志。你有没有注意到日志行
[Byte Buddy] INSTALL HELLO BYTE BUDDY! on HELLO BYTE BUDDY!
正在打印两个明显使用您刚才建议的方法的对象?所以要小心!