How to advise a method after this method has been intercepted.
See original GitHub issueHi, I found it does not work to advise a method after this method has been intercepted. Here is demo code :
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder.Listener;
import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.utility.JavaModule;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import static net.bytebuddy.matcher.ElementMatchers.named;
public class Main {
private static final InstrumentationProvider provider = InstrumentationProviderImpl.INSTANCE;
public static void main(String[] args) {
Instrumentation instrumentation = provider.findInstrumentation();
AgentBuilder.Identified.Extendable iExtendable = null;
ResettableClassFileTransformer iResettable = null;
AgentBuilder.Default iBuilder = new AgentBuilder.Default();
iExtendable = iBuilder.type(named("Person"))
.transform((builder, typeDescription, classLoader, module) -> {
builder = builder.method(named("say")).intercept(MethodDelegation.to(CallInterceptor.class));
return builder;
});
iResettable = iExtendable.with(new Listener.WithTransformationsOnly(Listener.StreamWriting.toSystemOut()) {
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
super.onTransformation(typeDescription, classLoader, module, loaded, dynamicType);
outputClassContentToFile(typeDescription, dynamicType, "interceptor");
}
})
.installOn(instrumentation);
System.out.println("before agent");
Person person = new Person();
person.say();
showLoadedClass(instrumentation);
AgentBuilder.Identified.Extendable extendable = null;
ResettableClassFileTransformer resettable = null;
AgentBuilder.Default builder = new AgentBuilder.Default();
extendable = builder.type(named("Person"))
.transform((innerBuilder, typeDescription, classLoader, module) -> {
innerBuilder = innerBuilder.visit(
Advice.to(CallAdvisor.class)
.on(named("say")));
return innerBuilder;
});
resettable = extendable.with(new Listener.WithTransformationsOnly(Listener.StreamWriting.toSystemOut()) {
@Override
public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
super.onTransformation(typeDescription, classLoader, module, loaded, dynamicType);
outputClassContentToFile(typeDescription, dynamicType, "advisor");
}
})
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.installOn(instrumentation);
System.out.println("after agent used");
person.say();
showLoadedClass(instrumentation);
if (resettable != null) {
resettable.reset(instrumentation,
AgentBuilder.RedefinitionStrategy.REDEFINITION,
AgentBuilder.RedefinitionStrategy.DiscoveryStrategy.Reiterating.INSTANCE,
AgentBuilder.RedefinitionStrategy.BatchAllocator.ForFixedSize.ofSize(1),
AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemOut()
);
}
System.out.println("after agent reset");
person.say();
}
}
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
public class CallInterceptor {
@RuntimeType
public static Object intercept(
@This(optional = true) Object origin,
@Origin(cache = false) Method method,
@SuperCall Callable<?> callable,
@AllArguments Object[] args
) throws Exception {
// need all of the parameters
System.out.println("intercept call");
return callable.call();
}
}
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bytecode.assign.Assigner;
import java.lang.reflect.Method;
public class CallAdvisor {
@Advice.OnMethodEnter
public static void onMethodEnter(@Advice.This(typing = Assigner.Typing.DYNAMIC) Object origin,
@Advice.Origin Method method,
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] arguments) {
System.out.println("call enter");
}
@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void onMethodExit(@Advice.This(typing = Assigner.Typing.DYNAMIC) Object origin,
@Advice.Origin Method method,
@Advice.AllArguments(typing = Assigner.Typing.DYNAMIC) Object[] arguments,
@Advice.Return(readOnly = true, typing = Assigner.Typing.DYNAMIC) Object ret,
@Advice.Thrown(readOnly = true, typing = Assigner.Typing.DYNAMIC) Throwable e) {
if (e != null) {
System.out.println(e.getMessage());
}
System.out.println("call left");
}
}
public class Person {
public void say() {
System.out.println("hello");
}
}
And here is the output:
before agent [Byte Buddy] TRANSFORM Person [sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=false] intercept call hello Person$auxiliary$GnBTihZL loaded after agent Person loaded after agent [Byte Buddy] TRANSFORM Person [sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=true] [Byte Buddy] TRANSFORM Person [sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=true] after agent used intercept call hello Person$auxiliary$GnBTihZL loaded after agent Person loaded after agent [Byte Buddy] REDEFINE BATCH #0 [1 of 1 type(s)] [Byte Buddy] TRANSFORM Person [sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=true] [Byte Buddy] REDEFINE ERROR #0 [1 of 1 type(s)] java.lang.UnsupportedOperationException: class redefinition failed: attempted to delete a method at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method) at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170) at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector$ForRedefinition.doApply(AgentBuilder.java:7165) at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy$Collector.apply(AgentBuilder.java:7032) at net.bytebuddy.agent.builder.AgentBuilder$RedefinitionStrategy.apply(AgentBuilder.java:4868) at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.reset(AgentBuilder.java:10395) at net.bytebuddy.agent.builder.ResettableClassFileTransformer$AbstractBase.reset(ResettableClassFileTransformer.java:358) at Main.main(Main.java:87) [Byte Buddy] REDEFINE COMPLETE 1 batch(es) containing 1 types [1 failed batch(es)] [Byte Buddy] REDEFINE COMPLETE 1 batch(es) containing 0 types [0 failed batch(es)] after agent reset intercept call hello
The output show that the CallAdvisor does not work.
Is there any way to advise a method after this method has been intercepted.
Issue Analytics
- State:
- Created 3 years ago
- Comments:9 (3 by maintainers)
I see, REDEFINITION is only recommended in few cases or when using Java 5. Good it worked out!
yes,it worked,thank you for your help