byte-buddy
byte-buddy copied to clipboard
Advice can't work with constructor method in javaagent
I try to use the advice to intercept all the methods excluding constructor method, the code below works well
public static void main(String[] args) {
ByteBuddyAgent.install();
new ByteBuddy().redefine(OrderController.class)
.visit(Advice.to(MethodTimeCostAdvice.class).on(isDeclaredBy(OrderController.class)
.and(not(isConstructor()))))
.make().load(ClassLoader.getSystemClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
new OrderController().hello3();
}
the OrderController:
public class OrderController {
Object lock1 = new Object();
Object lock2 = new Object();
public String hello1() throws InterruptedException {
synchronized (lock1) {
Thread.sleep(10000);
synchronized (lock2) {
return "hello1, it is nice to meet you !";
}
}
}
public String hello2() throws InterruptedException {
synchronized (lock2) {
Thread.sleep(10000);
synchronized (lock1) {
return "hello1, it is nice to meet you !";
}
}
}
public String hello3() {
System.out.println("hello3");
return "hello3";
}
}
but I change it to
public static void main(String[] args) {
new OrderController().hello3();
}
and add the VM options to run : -javaagent:target/spring-object-trace-agent-1.0.jar. I use the IDE debug find there is a error:
java.lang.IllegalStateException: Cannot represent static com.example.orderdemo.controller.OrderController() as given method constant
here is the agent code and interceptor code:
public class MethodTimeCostAdvice {
@Advice.OnMethodEnter
public static long enter(){
return System.currentTimeMillis();
}
@Advice.OnMethodExit
public static void exit(@Advice.Enter long startTime,
@Advice.Origin Method method,
@Advice.AllArguments Object[] args,
@Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
@Advice.This Object instance){
long cost = System.currentTimeMillis() - startTime;
String methodSignature = getMethodSignature(instance, method, args);
System.out.println(String.format("方法: %s \n耗时: %dms \n入参: %s \n返回结果: %s", methodSignature, cost, getArgs(args),result));
}
public static String getArgs(Object[] args){
if(args.length == 0){
return "void";
}
return Arrays.stream(args).map(Object::toString).collect(Collectors.joining(","));
}
public static String getMethodSignature(Object instance, Method method, Object[] parameters) {
StringBuilder builder = new StringBuilder();
builder.append(instance.getClass().getCanonicalName());
builder.append(".");
builder.append(method.getName());
builder.append("(");
for (int i = 0; i < parameters.length; i++) {
String[] split = parameters[i].getClass().getCanonicalName().split("\\.");
builder.append(split[split.length - 1]);
if (i != parameters.length - 1) {
builder.append(", ");
}
}
builder.append(")");
return builder.toString();
}
}
public class SpringObjectTraceAgent {
public static void premain(String args, Instrumentation instrument) {
System.out.println("SpringObjectTraceAgent start.... ");
new AgentBuilder.Default()
.with(AgentBuilder.TypeStrategy.Default.REDEFINE)
.type(ElementMatchers.nameStartsWith("com.example"))
.transform(new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule javaModule) {
return builder
.visit(Advice.to(MethodTimeCostAdvice.class)
.on(isDeclaredBy(typeDescription)
.and(not(isConstructor()))));
}
}).installOn(instrument);
}
}
I don't know why in this way it can't work.
My local env config:
JDK 1.8
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.12.10</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.12.10</version>
</dependency>
You are requesting a Method which cannot represent a Constructor. Rather use an Executable, or create two advices and match them accordingly.
Okay, thanks! Still, there is a question I want to ask: byteBuddy will catch the exception when redefining a class is not okay and it just give up the operation. for example:
public static void main(String[] args) {
premain(null, ByteBuddyAgent.install());
OrderController orderController = new OrderController();
orderController.hello3();
}
Actually, I want to throw the exception when I am testing the code so I can know what is wrong quickly. Now I get what error messages are through the IDE debug tool, which is very inefficient for me.
It's the instrumentation API that catches the exception. Byte Buddy cannot do anything here.
You can however register a listener and get the exception from there. You would then need to make sure to fail your test.
You are requesting a Method which cannot represent a Constructor. Rather use an Executable, or create two advices and match them accordingly.
Sorry to bother but how to correct matcher in the demo, I have met the same problem.