自定义 Tomcat Web 应用程序类加载器

发布于 2024-10-09 09:38:59 字数 726 浏览 1 评论 0原文

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

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

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

I'm trying to implement a custom classloader for tomcat. My first attempt yielded a class cast exception (apparently, tomcat tries to cast my loader to org.apache.catalina.loader.WebappLoader). Fine, I extended WebappLoader and added the catalina.jar to my buildpath.

Now I'm ready to deploy (I think). I'm getting this error:

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

Tomcat comes with catalina.jar to run, so I'm 99.9% sure it's already loaded into tomcat. I verified this by checking /server/lib/catalina.jar, which does contain the apache WebappLoader. Furthermore, manually linking another catalina.jar in creates a whole mess of problems, as expected.

I'm confused. Any tips would be hot.

Thanks!

Update: Interestingly, the same thing on tomcat6 (extending WebappLoader; worked on tomcat5.5), still causes a ClassCastException. Sounds to me like the class performing the cast was loaded using a different loader from the one which loaded my class. I don't see how I would have control over that anyway, unless maybe another missing tomcat config somewhere? Any ideas for tomcat6 either?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夜巴黎 2024-10-16 09:38:59

也许我太笨了,但我认为应该是WebappClassLoader,而不是WebappLoader。不过进口看起来还不错。

Maybe I'm being dense, but I think that should be WebappClassLoader, not WebappLoader. The import looks ok though.

丶视觉 2024-10-16 09:38:59

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

场景是这样的:

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

Without knowing the specifics of your code & setup it's impossible to be sure, but it reminds me of a problem I encountered when trying my hand at custom classloaders.

The scenario is this:

  1. The boot loader (JVM proper/Tomcat/whichever) loads your code
  2. Your classloader loads your additions not available on the classpath of the above.
  3. Your code references those additions.
  4. Those additions are not available in the same namespace as the code loaded by the boot loader.
  5. Your code in the boot loader's namespace runs, attempts to reference code in your custom namespace and this is not visible from the boot loader's namespace. So at that point the JVM bails with the NoClassDefFound error.

The reason for this is that the classloader hierachy works in a single direction only: i.e. code from sub-namespaces (child classloaders) is not available (visible) in the broader parent namespace (parent classloader); and there is no good way to hack into this system.

Fortunately, you can define interfaces which are available in the parent namespace, then implement these in code only visible in the sub-namespace. Then, code that lives inside the parent namespace only is able to use this interface name for casting & method call purposes and thus access your sub-namespace component regardless.

In order for this to work, you must ensure that your custom classloader does not only load components that the parent loader has no access to (i.e. are outside of its classpath), but also those bits of your code which directly interfaces with these components and explicitly refer these symbols (typenames/methods etc.). Otherwise references to these components end up in the parent namespace (remember, the boot classloader loads all your own code by default) and you're back to the original problem.

You can do this by subverting the intended classloading delegation model. Normally, you would defer to the parent loader before attempting to load classes yourself. However, now you must check in advance that your code does not touch any of these components unvailable to the parent loader. The easiest way is probably to set up your code path in such a way that the classloader maintains a set of classnames which it is to load itself rather than allow the parent loader to load them.

You have to find a way to tell your custom classloader somehow, a type annotation on class declarations could be used for this. The idea here is that your classloader introspects classes loaded by the parent, and if it finds a particular custom annotation on the type name it will invoke methods on the annotation to get the name of the class symbols that it must not allow its parent loader to load.

Example:

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

Note that because the class MyFeatureProvider will be loaded before MyImpl is, your classloader will know in advance about the annotation in MyFeatureProvider so it will be able to override the default delegation model for MyImpl. Because the rest of your code only interacts with MyImpl as an instance of MyFeature the parent loader never need to balk at the sight of undefined symbols -- and the ClassNoDefFound error is solved.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文