0

好的,所以这对你们来说是某种理论问题。

我正在试验 cglib 的 Enchancer - 为一个类创建一个代理。我的代码在 Felix OSGi 容器中运行。

层次结构看起来类似于:

// Bundle A;
// Imports-Package: javax.xml.datatype
// Exports-Package: a.foo

package a.foo;
public class Parent {
   protected javax.xml.datatype.XMLGregorianCalendar foo;

   ... -> getter/setter;
}

// Bundle B
// Imports-Package: a.foo
// DOES NOT IMPORT PACKAGE javax.xml.datatype !!!

package b.bar;

import a.foo.Parent;
public class Child extends Parent {
   protected String bar;

   ... -> getter/setter;
}

// Bundle B

// Code extracted from https://github.com/modelmapper/modelmapper/blob/master/core/src/main/java/org/modelmapper/internal/ProxyFactory.java#L59
    public Child enchance() {
         Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(Child.class);
         enhancer.setUseFactory(true);
         enhancer.setUseCache(true);
         enhancer.setNamingPolicy(NAMING_POLICY);
         enhancer.setCallbackFilter(METHOD_FILTER);
         enhancer.setCallbackTypes(new Class[] { MethodInterceptor.class, NoOp.class });

         try {
             return enhancer.createClass();
         } catch (Throwable t) {
             t.printStackTrace();
     }
 } 

从 OSGi 的角度来看 - 两个捆绑包 - 捆绑包 A 和捆绑包 B 功能齐全。包导入/导出是 bnd 生成的。尽管 BundleA 没有明确导入 javax.xml.datatype 包 - 我可以毫无问题地创建 Child 的实例。到现在为止还挺好。

但是当我尝试调用 enchance() 方法并创建子代理时 - cglib 抛出 NoClassDefFoundError: javax.xml.datatype.XMLGregorianCalendar

好的,我明白了——BundleB 的类加载器确实无法加载这个类,事实上——cglib 的 Enchancer 似乎正在使用 BundleB 的类加载器(Child 的类类型类加载器)来创建代理。

另一方面——为了处理模块化,OSGi 容器正在执行所谓的类加载委托——而不是 BundleB 的类加载器,OSGi 运行时将父类 Parent 的加载委托给 BundleA 的类加载器,后者知道如何加载其所有字段。这就是为什么 BundleB 不需要显式导入 javax.xml.datatype 包并且不需要知道如何加载 XMLGregorianCalendar 类并且仍然能够使用 Child 对象的原因。

我想知道 - 这种“委托”方法是否也适用于 cglib 的用例?请注意,我对字节码操作一无所知,这对某些人来说可能是一个非常愚蠢的问题。但我真的不明白 - 为什么 cglib 不能将 Parent 的加载委托给 Parent 自己的类加载器?cglib中真的没有这种机制吗?为什么?cglib 不与 OSGi 结合使用吗?如果是,那为什么?

4

2 回答 2

0

该类Child不需要导入javax.xml.datatype,只要它不访问该javax.xml.datatype.XMLGregorianCalendar字段并且您只是以Child正常方式使用该类。然而,为了生成代理类,CGLib 需要了解完整继承层次结构的内部结构,包括javax.xml.datatype.XMLGregorianCalendar生成新类型的字节码。因此需要导入包。

不幸的是,bnd 无法预测您将在Child该类上生成字节码,因此它不会添加导入javax.xml.datatype——它只会添加正常使用所需的导入。

一般来说,从另一个包中导入的类继承是一个坏主意。Java 继承创建了从子类到超类的非常紧密的耦合,这意味着您可以接触到超类的内部结构。

对于你的最后一个问题:CGLib 在 OSGi 中相当广泛地用于在测试期间模拟对象之类的事情。它在生产中使用较少,因为几乎总是有比字节码生成更好的解决方案,例如正确使用服务注册表。

于 2017-12-18T09:18:48.417 回答
0

我尝试结合此处描述的 OSGi 类加载器桥的想法: https ://www.infoq.com/articles/code-generation-with-osgi ...解决了在 OSGi 中运行的代码生成框架的类似问题,与我最近想到的另一个想法。

这个想法是跟踪在用户类型的父类型层次结构中找到的类类型的类加载器。我们稍后可以使用这些类加载器作为加载类型的后备,否则 Bundle 的用户类型的类加载器不知道这些类型。

然后我们可以告诉 CGLIB 的 Enhancer 使用这个新的类加载器进行解析。

这个想法在这里提出: https ://github.com/modelmapper/modelmapper/pull/294

不过,我很想听听经验丰富的 OSGi 专家对此的看法。但到目前为止,这似乎奏效了。在被证明是错误的之前,我接受我自己的答案。

于 2017-12-19T13:14:48.847 回答