Я пытаюсь в основном реализовать графы глубокой насмешки классов (которые я не буду контролировать в дикой природе) для записи того, что вызывается. Быстрая и грязная версия, использующая просто Mockito, была на удивление надежной, но я наткнулся на стену, пытаясь реализовать ее «правильно» (и не заставлять людей задавать смешные вопросы, зачем им mockito в classpath времени выполнения). Поскольку это scala, конструкторы по умолчанию неслыханны, нулевые значения очень редко передаются изящно, а глубоко вложенные классы-члены являются нормой, поэтому, когда мне нужно применить класс к классу, мне нужно предоставить фактические аргументы для конструктора, требуя от меня на инструмент те по очереди. Соответствующие фрагменты из моего кода:
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
.
. В результате я получаю NoClassDefFoundError
во время load
(LoadedTypeInitializer$ForStaticField.onLoad()
), поскольку каждый класс загружается со своим собственным загрузчиком классов. К сожалению, хотя причина была сразу очевидна, это не помогло мне попытаться заставить ByteBuddy совместно использовать загрузчик классов или каким-либо другим образом сделать эти классы доступными. Я играл со всеми предоставленными аргументами make
, load
, но безрезультатно; все вызовы имеют общую стратегию разрешения типов TypePool
- ничего, кроме INJECTION ClassLoaderStrategy, которую я не хочу использовать из-за ее зависимости от частных API-интерфейсов, которые не позволили бы инвестировать мои усилия в эту стратегию.
Кажется, это очень простая c проблема, которую легко решить, но я просмотрел множество примеров кода из других проектов и не вижу ничего, что они делают по-другому, что должно иметь какое-то значение. Идеи?