4

我正在尝试为 tomcat 实现自定义类加载器。我的第一次尝试产生了一个类转换异常(显然,tomcat 试图将我的加载器转换为 org.apache.catalina.loader.WebappLoader)。很好,我扩展了 WebappLoader 并将 catalina.jar 添加到我的构建路径中。

现在我已经准备好部署(我认为)。我收到此错误:

严重:Catalina.start: LifecycleException: start: : java.lang.NoClassDefFoundError: org/apache/catalina/loader/WebappLoader

Tomcat 自带了 catalina.jar 来运行,所以我有 99.9% 的把握它已经加载到了 tomcat 中。我通过检查 /server/lib/catalina.jar 验证了这一点,它确实包含 apache WebappLoader。此外,正如预期的那样,手动链接另一个 catalina.jar 会产生一大堆问题。

我很困惑。任何提示都会很热门。

谢谢!

更新:有趣的是,同样的事情在 tomcat6 上(扩展 WebappLoader;在 tomcat5.5 上工作)仍然会导致 ClassCastException。在我看来,执行演员表的类是使用与加载我的类的加载器不同的加载器加载的。无论如何,我看不出我将如何控制它,除非在某个地方可能有另一个丢失的 tomcat 配置?对tomcat6有什么想法吗?

4

2 回答 2

5

也许我很密集,但我认为应该是 WebappClassLoader,而不是 WebappLoader。导入看起来不错。

于 2010-12-27T01:00:58.350 回答
1

如果不知道您的代码和设置的细节,就不可能确定,但​​这让我想起了我在尝试自定义类加载器时遇到的一个问题。

场景是这样的:

  1. 引导加载程序(JVM 正确/Tomcat/任何一个)加载您的代码
  2. 您的类加载器会加载上述类路径中不可用的添加内容。
  3. 您的代码引用了这些添加。
  4. 这些添加在与引导加载程序加载的代码相同的命名空间中不可用。
  5. 引导加载程序命名空间中的代码运行,尝试引用自定义命名空间中的代码,这在引导加载程序的命名空间中不可见。因此,此时 JVM 因 NoClassDefFound 错误而退出。

这样做的原因是类加载器层次结构仅在一个方向上工作:即来自子命名空间(子类加载器)的代码在更广泛的父命名空间(父类加载器)中不可用(可见);而且没有什么好的方法可以侵入这个系统。

幸运的是,您可以定义在父命名空间中可用的接口,然后在仅在子命名空间中可见的代码中实现这些接口。然后,仅位于父命名空间内的代码能够使用此接口名称进行强制转换和方法调用,因此无论如何都可以访问您的子命名空间组件。

为了使它工作,您必须确保您的自定义类加载器不仅加载父加载器无法访问的组件(即在其类路径之外),而且还加载那些直接与这些组件交互的代码位和明确引用这些符号(类型名/方法等)。否则,对这些组件的引用最终会出现在父命名空间中(请记住,默认情况下,引导类加载器会加载您自己的所有代码),而您又回到了最初的问题。

您可以通过颠覆预期的类加载委托模型来做到这一点。通常,在尝试自己加载类之前,您会遵循父加载器。但是,现在您必须提前检查您的代码是否不会触及任何对父加载器不可用的组件。最简单的方法可能是设置您的代码路径,使类加载器维护一组类名,以便自己加载而不是允许父加载器加载它们。

您必须找到一种方法以某种方式告诉您的自定义类加载器,可以为此使用类声明上的类型注释。这里的想法是您的类加载器内省由父加载器加载的类,如果它在类型名称上找到特定的自定义注释,它将调用注释上的方法来获取它不能允许其父加载器的类符号的名称加载。

例子:

@MyCustomAnnotation(implementation="my.custom.package.MyImpl")
public class MyFeatureProvider {
  public MyFeature getFeature() { // return an instance of MyImpl here }
}

请注意,由于该类MyFeatureProvider将在之前 MyImpl加载,因此您的类加载器将提前知道其中的注释,MyFeatureProvider因此它将能够覆盖 MyImpl 的默认委托模型。MyImpl因为您的其余代码仅作为父加载器的实例进行交互,MyFeature所以永远不需要看到未定义的符号 - 并且 ClassNoDefFound 错误已解决。

于 2010-12-26T22:21:31.303 回答