如何使用 ByteBuddy 代理处理构造函数抛出的异常?
How to take the exception thrown by a constructor using a ByteBuddy agent?
我正在尝试 记录每次调用、返回的对象和异常 使用 ByteBuddy (v1.7.9 ) java 代理,不会干扰检测代码的正常运行。
我当前的代理实例是
new AgentBuilder.Default()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut())
.type((typeDescription, classLoader, module, classBeingRedefined, protectionDomain) ->
matcher.matchesIncoming(typeDescription.getTypeName()))
.transform((builder, typeDescription, classLoader, javaModule) -> builder
.visit(Advice.to(CustomAdvicer.class).on(ElementMatchers.any())))
.installOn(inst);
我从最简单的开始 "advicer",
public class CustomAdvicer {
@Advice.OnMethodEnter
public static void enter(@Advice.Origin String origin) {
System.out.println("Entering " + origin);
}
@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void exit(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) @RuntimeType Object value,
@Advice.Origin String origin,
@Advice.Thrown Throwable thrown) {
System.out.println("Exiting " + origin);
}
}
然而,当我 运行 程序时,我从 bytebuddy 得到一个异常:
[Byte Buddy] ERROR some.pack.Thrower [sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=false]
java.lang.IllegalStateException: Cannot catch exception during constructor call for public some.pack.Thrower() throws java.lang.Exception
at net.bytebuddy.asm.Advice.doWrap(Advice.java:515)
at net.bytebuddy.asm.Advice.wrap(Advice.java:470)
at net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$Entry.wrap(AsmVisitorWrapper.java:481)
at net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$DispatchingVisitor.visitMethod(AsmVisitorWrapper.java:562)
at net.bytebuddy.jar.asm.ClassVisitor.visitMethod(ClassVisitor.java:327)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$RedefinitionClassVisitor.visitMethod(TypeWriter.java:3801)
at net.bytebuddy.jar.asm.ClassReader.readMethod(ClassReader.java:1020)
at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:698)
at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:500)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2941)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1633)
at net.bytebuddy.dynamic.scaffold.inline.RebaseDynamicTypeBuilder.make(RebaseDynamicTypeBuilder.java:200)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:8905)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:9306)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9269)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access00(AgentBuilder.java:9047)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9625)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9575)
at java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9194)
at sun.instrument.TransformerManager.transform(Unknown Source)
at sun.instrument.InstrumentationImpl.transform(Unknown Source)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access0(Unknown Source)
at java.net.URLClassLoader.run(Unknown Source)
at java.net.URLClassLoader.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at Main.main(Main.java:23)
那么,我应该怎么做才能记录构造函数中抛出的异常,记住它不能干扰原始代码?
顺便说一句,Thrower class 很傻 class 我写信来测试这个案例:
package some.pack;
public class Thrower {
public Thrower() throws Exception {
throw new Exception("By courtesy of thrower! ;)");
}
}
问题是构造函数有一个隐含的第一条指令,它是调用另一个构造函数或超级构造函数。你的 Thrower
class 真的是这样的:
public class Thrower {
public Thrower() throws Exception {
super();
throw new Exception("By courtesy of thrower! ;)");
}
}
如果您想将整个调用包装在一个 try-catch 块中,这将产生:
public class Thrower {
public Thrower() throws Exception {
try {
super();
throw new Exception("By courtesy of thrower! ;)");
} catch (Exception e) {
...
}
}
}
但这在JVM 中是不合法的,因此字节好友不允许这样做。没有排除超级构造函数调用的好方法,因为这是第一次调用只是 Java 语言约定,但字节码允许更多的任意组合。因为你不知道class是什么语言,所以字节好友在这里不尝试任何技巧,根本不允许
我正在尝试 记录每次调用、返回的对象和异常 使用 ByteBuddy (v1.7.9 ) java 代理,不会干扰检测代码的正常运行。
我当前的代理实例是
new AgentBuilder.Default()
.with(AgentBuilder.Listener.StreamWriting.toSystemOut())
.type((typeDescription, classLoader, module, classBeingRedefined, protectionDomain) ->
matcher.matchesIncoming(typeDescription.getTypeName()))
.transform((builder, typeDescription, classLoader, javaModule) -> builder
.visit(Advice.to(CustomAdvicer.class).on(ElementMatchers.any())))
.installOn(inst);
我从最简单的开始 "advicer",
public class CustomAdvicer {
@Advice.OnMethodEnter
public static void enter(@Advice.Origin String origin) {
System.out.println("Entering " + origin);
}
@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void exit(
@Advice.Return(typing = Assigner.Typing.DYNAMIC) @RuntimeType Object value,
@Advice.Origin String origin,
@Advice.Thrown Throwable thrown) {
System.out.println("Exiting " + origin);
}
}
然而,当我 运行 程序时,我从 bytebuddy 得到一个异常:
[Byte Buddy] ERROR some.pack.Thrower [sun.misc.Launcher$AppClassLoader@18b4aac2, null, loaded=false]
java.lang.IllegalStateException: Cannot catch exception during constructor call for public some.pack.Thrower() throws java.lang.Exception
at net.bytebuddy.asm.Advice.doWrap(Advice.java:515)
at net.bytebuddy.asm.Advice.wrap(Advice.java:470)
at net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$Entry.wrap(AsmVisitorWrapper.java:481)
at net.bytebuddy.asm.AsmVisitorWrapper$ForDeclaredMethods$DispatchingVisitor.visitMethod(AsmVisitorWrapper.java:562)
at net.bytebuddy.jar.asm.ClassVisitor.visitMethod(ClassVisitor.java:327)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$RedefinitionClassVisitor.visitMethod(TypeWriter.java:3801)
at net.bytebuddy.jar.asm.ClassReader.readMethod(ClassReader.java:1020)
at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:698)
at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:500)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:2941)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1633)
at net.bytebuddy.dynamic.scaffold.inline.RebaseDynamicTypeBuilder.make(RebaseDynamicTypeBuilder.java:200)
at net.bytebuddy.agent.builder.AgentBuilder$Default$Transformation$Simple$Resolution.apply(AgentBuilder.java:8905)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:9306)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9269)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.access00(AgentBuilder.java:9047)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9625)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer$LegacyVmDispatcher.run(AgentBuilder.java:9575)
at java.security.AccessController.doPrivileged(Native Method)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.transform(AgentBuilder.java:9194)
at sun.instrument.TransformerManager.transform(Unknown Source)
at sun.instrument.InstrumentationImpl.transform(Unknown Source)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access0(Unknown Source)
at java.net.URLClassLoader.run(Unknown Source)
at java.net.URLClassLoader.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at Main.main(Main.java:23)
那么,我应该怎么做才能记录构造函数中抛出的异常,记住它不能干扰原始代码?
顺便说一句,Thrower class 很傻 class 我写信来测试这个案例:
package some.pack;
public class Thrower {
public Thrower() throws Exception {
throw new Exception("By courtesy of thrower! ;)");
}
}
问题是构造函数有一个隐含的第一条指令,它是调用另一个构造函数或超级构造函数。你的 Thrower
class 真的是这样的:
public class Thrower {
public Thrower() throws Exception {
super();
throw new Exception("By courtesy of thrower! ;)");
}
}
如果您想将整个调用包装在一个 try-catch 块中,这将产生:
public class Thrower {
public Thrower() throws Exception {
try {
super();
throw new Exception("By courtesy of thrower! ;)");
} catch (Exception e) {
...
}
}
}
但这在JVM 中是不合法的,因此字节好友不允许这样做。没有排除超级构造函数调用的好方法,因为这是第一次调用只是 Java 语言约定,但字节码允许更多的任意组合。因为你不知道class是什么语言,所以字节好友在这里不尝试任何技巧,根本不允许