I am using Byte Buddy to intercept some JDK methods, System.class and Thread.class is well, but is not work on java.lang.Object. I am running my test on JDK8 and it doesn't throw any errors.
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();
}
}
}
And I try to use Javassist, transform java.lang.Object's method is successful. Could anyone konw why does it not work on Object.class?
You want to use
disableClassFormatChanges()
in order to avoid structural class file changes which would violate retransformation rules,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());
}
}
This works, ...
[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!
... but I think you should think twice before manipulating JDK core classes. Look at the above log. Do you notice how in log line
[Byte Buddy] INSTALL HELLO BYTE BUDDY! on HELLO BYTE BUDDY!
two objects are being printed which obviously use the method you have just advised? So be careful!