【问题标题】:Custom Tomcat Webapp ClassLoader自定义 Tomcat Webapp 类加载器
【发布时间】:2010-12-26 20:31:13
【问题描述】:

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

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

严重:Catalina.start: LifecycleException:开始:: 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 有什么想法吗?

【问题讨论】:

    标签: java tomcat tomcat6 classloader contextclassloader


    【解决方案1】:

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

    【讨论】:

    • 天啊;不敢相信我做到了。谢谢!现在可以使用了。
    【解决方案2】:

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

    场景是这样的:

    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 的模型。因为您的其余代码仅作为 MyFeature 的实例与 MyImpl 交互,所以父加载器永远不需要看到未定义的符号 - 并且 ClassNoDefFound 错误已解决。

    【讨论】:

      猜你喜欢
      • 2017-04-09
      • 1970-01-01
      • 2012-03-08
      • 2015-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-11
      • 2023-03-07
      相关资源
      最近更新 更多