Java 截取构造函数导致ClassNotFoundException
我试图截取用Java 截取构造函数导致ClassNotFoundException,java,bytecode,instrumentation,byte-buddy,Java,Bytecode,Instrumentation,Byte Buddy,我试图截取用@Inject注释的构造函数。在一个小单元测试的环境中,这工作得很好。然而,在像Spring这样的DI容器的上下文中,它会失败,出现ClassNotFoundException 我设法缩小了根本原因的范围。对插入指令的类调用getDeclaredConstructors将触发此异常。有趣的是,如果我们首先创建该类的实例,问题就会消失 例如: public class InterceptConstructorTest { @Test public void testC
@Inject
注释的构造函数。在一个小单元测试的环境中,这工作得很好。然而,在像Spring这样的DI容器的上下文中,它会失败,出现ClassNotFoundException
我设法缩小了根本原因的范围。对插入指令的类调用getDeclaredConstructors
将触发此异常。有趣的是,如果我们首先创建该类的实例,问题就会消失
例如:
public class InterceptConstructorTest {
@Test
public void testConstructorInterception() throws ClassNotFoundException {
ByteBuddyAgent.install();
new AgentBuilder.Default().type(nameStartsWith("test")).transform(new AgentBuilder.Transformer() {
@Override
public Builder<?> transform(Builder<?> builder, TypeDescription td) {
return builder.constructor(isAnnotatedWith(Inject.class))
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(ConstructorInterceptor.class)));
}
}).installOnByteBuddyAgent();
// If this line is uncommented, ClassNotFoundException won't be thrown
// MyClass myClass = new MyClass("a param");
// Manually load MyClass
Class<?> myClassDefinition = getClass().getClassLoader().loadClass("test.MyClass");
// Throws NoClassDefFoundError
for(Constructor<?> constructor : myClassDefinition.getDeclaredConstructors()) {
System.out.println(constructor);
}
}
}
本例中的问题是构造函数注入。为了重新设置构造函数的基础,Byte Buddy需要创建一个附加类型并创建一个类,如下所示:
class MyClass {
private synthetic MyClass(String aParam, $SomeType ignored) {
System.out.println("constructor called");
}
@Inject
public MyClass(String aParam) {
this(aParam, null);
// Instrumentation logic.
}
}
不幸的是,额外的类型对于为重基构造函数创建唯一签名是必需的。对于方法,Byte Buddy可以更改名称,但对于构造函数,这是不可能的,因为它们必须在类文件中被命名为
,才能被识别为构造函数
Byte Buddy尝试仅在插入类型之后加载辅助类。根据虚拟机的不同,加载引用另一个类的类会导致加载引用的类型。如果此类型是已检测的类,则检测将中止循环的正在进行的检测
因此,Byte Buddy确保任何辅助类型仅在能够确保加载插入指令的类型后的第一个可能点加载。它通过在插入指令的类的类初始值设定项中添加一个自初始化来实现这一点。在某种程度上,Byte Buddy添加了一个块:
static {
ByteBuddy.loadAuxiliaryTypes(MyClass.class);
}
如果在类上反射之前未执行此块,则不会加载辅助类型,并引发遇到的异常。如果你打电话:
Class.forName("test.MyClass", true, getClass().getClassLoader());
如果第二个参数指示急切地执行类初始值设定项,则不会出现问题,而不是loadClass
。此外,如果创建实例,则会执行初始值设定项
当然,这并不令人满意,我现在添加一些逻辑来决定辅助类型是否可以在检测期间加载以避免此类错误。自版本0.7.7以来,Byte Buddy的新默认
初始化策略
为您解决了这个问题。新版本当前与Maven Central Repository同步。在移动到0.7.7之后,在执行main
之前,我的应用程序会提前退出。没有提出例外情况。你知道我该如何帮助你解决这个问题吗?即使仪器出现故障,也不应该发生这种情况。您始终可以添加一个AgentBuilder.Listener
,以检查Byte Buddy是否发出错误。可能您的代理仍在使用旧版本的Byte Buddy,导致出现NoClassDefFoundError
?您的单元测试可以与新版本一起工作吗?如果是这样的话,我假设您在某个地方有版本冲突。您知道发生了什么吗?很抱歉延迟。我的测试仍然以0.7.7通过。但是,在一个更复杂的程序中,我有AgentBuilder.Default().type(any())
,它会过早退出。没有提出任何例外情况。并且,AgentBuilder.Listener
未报告任何错误。它在转换应用于sun.management.agentConfiguration Error
后立即消失。我必须承认,我使用any
matcher有点过火了。我将其更改为不转换java/javax/sun类,问题就消失了。不确定你是否想让我进一步调查?(我只是不知道如何从那里开始)
Class.forName("test.MyClass", true, getClass().getClassLoader());