我的 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 可用。我尝试了 makeload 提供的所有参数,但无济于事;让所有调用共享一个 TypePool,不同的类型解析策略——除了 INJECTION ClassLoaderStrategy 之外,我不想使用它,因为它依赖于私有 API,这不会让我在这个战略上投入精力。

这似乎是一个非常基本的问题,也很容易解决,但我浏览了其他项目的许多代码示例,但我看不出他们所做的任何不同应该有什么不同。想法?

很有可能与您使用ClassLoadingStrategy.Default.WRAPPER_PERSISTENT有关。使用此策略,classes 被加载到一个隔离的 class 加载器中,这使得 classes 对任何未继承该 classes class 加载器的人不可见。

为了加载一组 classes,您可能希望合并所有未加载的 classes (.merge) 并将它们一起加载到单个 class 加载程序中。

请注意,您也可以自己创建一个 ByteArrayClassLoader 并将其打开以供注入。这样以后可以使用注入 class 加载策略添加 classes。