自定义Tomcat Webapp ClassLoader

时间:2010-12-26 20:31:14

标签: java tomcat tomcat6 classloader contextclassloader

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

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

  

严重:Catalina.start:   LifecycleException:start ::   java.lang.NoClassDefFoundError:   组织/阿帕奇/卡塔利娜/装载器/ WebappLoader

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

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

谢谢!

更新:有趣的是,tomcat6上的相同内容(扩展WebappLoader;在tomcat5.5上运行)仍会导致ClassCastException。听起来像执行演员表的类是使用与加载我的类的装载器不同的加载器加载的。我不知道我怎么能控制它,除非在某处可能有另一个丢失的tomcat配置? tomcat6有什么想法吗?

2 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:1)

不知道您的代码的具体细节&设置它是不可能确定的,但它让我想起了在自定义类加载器上尝试时遇到的问题。

情景如下:

  1. 引导加载程序(JVM本身/ Tomcat /取其中)加载代码
  2. 您的类加载器会加载上面类路径中没有的附加内容。
  3. 您的代码引用了这些添加内容。
  4. 这些添加项与引导加载程序加载的代码在同一名称空间中不可用。
  5. 您的引导加载程序命名空间中的代码运行,尝试引用自定义命名空间中的代码,这在引导加载程序的命名空间中是不可见的。所以在这一点上,JVM就会出现NoClassDefFound错误。
  6. 这样做的原因是类加载器层次结构仅在单一方向上工作:即子命名空间(子类加载器)中的代码在更宽的父命名空间(父类加载器)中不可用(可见);并没有很好的方法可以入侵这个系统。

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

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

    您可以通过破坏预期的类加载委派模型来实现此目的。通常,在尝试自己加载类之前,您会遵从父加载程序。但是,现在您必须提前检查您的代码是否未触及任何这些组件,这些组件对父加载器无效。最简单的方法可能是设置代码路径,使类加载器维护一组类加载自己的类名,而不是允许父加载器加载它们。

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

    示例:

    @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错误已经解决。