我的 ByteBuddy 生成 类 看不到对方
My ByteBuddy generated classes can't see each other
我尝试基本上实现 classes 的深度模拟图(我不会在野外控制)以记录正在调用的内容。简单地使用 Mockito 的快速而肮脏的版本出奇地健壮,但是我 运行 撞墙试图实现它 'properly' (并且没有人问有趣的问题为什么他们在运行时需要 mockito class小路)。
因为这是 scala,默认构造函数是闻所未闻的,空值很少被优雅地传递,深度嵌套的成员 classes 是一种常态,因此当我需要检测 class 时,我需要提供实际的构造函数的参数,要求我依次检测 those。我的代码中的相关片段:
private def createTracerClass(tpe :Type, clazz :Class[_]) :Class[_] = {
val name = clazz.getName + TracerClassNameSuffix + tpe.typeSymbol.fullName.replace('.', '_')
val builder =
if (clazz.isInterface) //todo: implement abstract methods!!!
ByteBuddy.subclass(clazz, new ForDefaultConstructor).name(name)
else {
val constructors = clazz.getDeclaredConstructors
.filter { c => (c.getModifiers & (PUBLIC | PROTECTED)) != 0 }.sortBy(_.getParameterCount)
if (constructors.isEmpty)
throw new PropertyReflectionException(
s"Can't instrument a tracer for class ${clazz.getName} as it has no accessible constructor."
)
val best = constructors.head
new ByteBuddy().subclass(clazz, NO_CONSTRUCTORS).name(name)
.defineConstructor(PUBLIC).intercept(
invoke(best).onSuper.`with`(best.getParameterTypes.map(createInstance):_*)
)
}
println("instrumenting " + name + "; class loader: "+clazz.getClassLoader)
val mockClass = builder
.method(not(isConstructor[MethodDescription]())).intercept(to(new MethodInterceptor()))
.defineMethod(TypeMethodName, classOf[AnyRef], PUBLIC).intercept(FixedValue.value(tpe))
.defineField(TraceFieldName, classOf[Trace], PUBLIC)
.make().load(getClassLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT).getLoaded
println("created class " + mockClass +"with classloader: " + mockClass.getClassLoader)
mockClass
}
private def instrumentedInstance(clazz :Class[_], modifiers :Int = PUBLIC | PROTECTED) :AnyRef =
if ((clazz.getModifiers & FINAL) != 0)
null
else {
val mockClass = MockCache.getOrElse(clazz,
clazz.synchronized {
MockCache.getOrElse(clazz, {
println("creating mock class for "+clazz.getName)
clazz.getDeclaredConstructors.filter { c => (c.getModifiers & modifiers) != 0 }
.sortBy(_.getParameterCount).headOption.map { cons =>
val subclass = ByteBuddy.subclass(clazz, NO_CONSTRUCTORS)
.name(clazz.getName + MockClassNameSuffix)
.defineConstructor(PUBLIC).intercept(
invoke(cons).onSuper.`with`(cons.getParameterTypes.map(createInstance) :_*)
).make().load(getClassLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT).getLoaded
MockCache.put(clazz, subclass)
subclass
}.orNull
})
}
)
println("creating a mock for " + clazz.getName + "; class loader: " + mockClass.getClassLoader)
mockClass.getConstructor().newInstance().asInstanceOf[AnyRef]
}
问题出在第一个方法底部的构造函数生成器中,它使用createInstance
创建。该方法又回到 instrumentInstance
.
结果是我在 load
(LoadedTypeInitializer$ForStaticField.onLoad()
) 期间得到一个 NoClassDefFoundError
,因为每个 class 都加载了自己的 class 加载器。不幸的是,虽然原因很明显,但它并没有帮助我尝试让 ByteBuddy 共享一个 class 加载器或以其他方式使那些 classes 可用。我尝试了 make
、load
提供的所有参数,但无济于事;让所有调用共享一个 TypePool
,不同的类型解析策略——除了 INJECTION ClassLoaderStrategy 之外,我不想使用它,因为它依赖于私有 API,这不会让我在这个战略上投入精力。
这似乎是一个非常基本的问题,也很容易解决,但我浏览了其他项目的许多代码示例,但我看不出他们所做的任何不同应该有什么不同。想法?
很有可能与您使用ClassLoadingStrategy.Default.WRAPPER_PERSISTENT有关。使用此策略,classes 被加载到一个隔离的 class 加载器中,这使得 classes 对任何未继承该 classes class 加载器的人不可见。
为了加载一组 classes,您可能希望合并所有未加载的 classes (.merge) 并将它们一起加载到单个 class 加载程序中。
请注意,您也可以自己创建一个 ByteArrayClassLoader 并将其打开以供注入。这样以后可以使用注入 class 加载策略添加 classes。
我尝试基本上实现 classes 的深度模拟图(我不会在野外控制)以记录正在调用的内容。简单地使用 Mockito 的快速而肮脏的版本出奇地健壮,但是我 运行 撞墙试图实现它 'properly' (并且没有人问有趣的问题为什么他们在运行时需要 mockito class小路)。 因为这是 scala,默认构造函数是闻所未闻的,空值很少被优雅地传递,深度嵌套的成员 classes 是一种常态,因此当我需要检测 class 时,我需要提供实际的构造函数的参数,要求我依次检测 those。我的代码中的相关片段:
private def createTracerClass(tpe :Type, clazz :Class[_]) :Class[_] = {
val name = clazz.getName + TracerClassNameSuffix + tpe.typeSymbol.fullName.replace('.', '_')
val builder =
if (clazz.isInterface) //todo: implement abstract methods!!!
ByteBuddy.subclass(clazz, new ForDefaultConstructor).name(name)
else {
val constructors = clazz.getDeclaredConstructors
.filter { c => (c.getModifiers & (PUBLIC | PROTECTED)) != 0 }.sortBy(_.getParameterCount)
if (constructors.isEmpty)
throw new PropertyReflectionException(
s"Can't instrument a tracer for class ${clazz.getName} as it has no accessible constructor."
)
val best = constructors.head
new ByteBuddy().subclass(clazz, NO_CONSTRUCTORS).name(name)
.defineConstructor(PUBLIC).intercept(
invoke(best).onSuper.`with`(best.getParameterTypes.map(createInstance):_*)
)
}
println("instrumenting " + name + "; class loader: "+clazz.getClassLoader)
val mockClass = builder
.method(not(isConstructor[MethodDescription]())).intercept(to(new MethodInterceptor()))
.defineMethod(TypeMethodName, classOf[AnyRef], PUBLIC).intercept(FixedValue.value(tpe))
.defineField(TraceFieldName, classOf[Trace], PUBLIC)
.make().load(getClassLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT).getLoaded
println("created class " + mockClass +"with classloader: " + mockClass.getClassLoader)
mockClass
}
private def instrumentedInstance(clazz :Class[_], modifiers :Int = PUBLIC | PROTECTED) :AnyRef =
if ((clazz.getModifiers & FINAL) != 0)
null
else {
val mockClass = MockCache.getOrElse(clazz,
clazz.synchronized {
MockCache.getOrElse(clazz, {
println("creating mock class for "+clazz.getName)
clazz.getDeclaredConstructors.filter { c => (c.getModifiers & modifiers) != 0 }
.sortBy(_.getParameterCount).headOption.map { cons =>
val subclass = ByteBuddy.subclass(clazz, NO_CONSTRUCTORS)
.name(clazz.getName + MockClassNameSuffix)
.defineConstructor(PUBLIC).intercept(
invoke(cons).onSuper.`with`(cons.getParameterTypes.map(createInstance) :_*)
).make().load(getClassLoader, ClassLoadingStrategy.Default.WRAPPER_PERSISTENT).getLoaded
MockCache.put(clazz, subclass)
subclass
}.orNull
})
}
)
println("creating a mock for " + clazz.getName + "; class loader: " + mockClass.getClassLoader)
mockClass.getConstructor().newInstance().asInstanceOf[AnyRef]
}
问题出在第一个方法底部的构造函数生成器中,它使用createInstance
创建。该方法又回到 instrumentInstance
.
结果是我在 load
(LoadedTypeInitializer$ForStaticField.onLoad()
) 期间得到一个 NoClassDefFoundError
,因为每个 class 都加载了自己的 class 加载器。不幸的是,虽然原因很明显,但它并没有帮助我尝试让 ByteBuddy 共享一个 class 加载器或以其他方式使那些 classes 可用。我尝试了 make
、load
提供的所有参数,但无济于事;让所有调用共享一个 TypePool
,不同的类型解析策略——除了 INJECTION ClassLoaderStrategy 之外,我不想使用它,因为它依赖于私有 API,这不会让我在这个战略上投入精力。
这似乎是一个非常基本的问题,也很容易解决,但我浏览了其他项目的许多代码示例,但我看不出他们所做的任何不同应该有什么不同。想法?
很有可能与您使用ClassLoadingStrategy.Default.WRAPPER_PERSISTENT有关。使用此策略,classes 被加载到一个隔离的 class 加载器中,这使得 classes 对任何未继承该 classes class 加载器的人不可见。
为了加载一组 classes,您可能希望合并所有未加载的 classes (.merge) 并将它们一起加载到单个 class 加载程序中。
请注意,您也可以自己创建一个 ByteArrayClassLoader 并将其打开以供注入。这样以后可以使用注入 class 加载策略添加 classes。