自定义 Tomcat Web 应用程序类加载器
我正在尝试为 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
也许我太笨了,但我认为应该是WebappClassLoader,而不是WebappLoader。不过进口看起来还不错。
Maybe I'm being dense, but I think that should be WebappClassLoader, not WebappLoader. The import looks ok though.
不知道您的代码的具体情况&设置它是不可能确定的,但它让我想起了我在尝试自定义类加载器时遇到的一个问题。
场景是这样的:
其原因是类加载器层次结构仅在一个方向上工作:即来自子命名空间(子类加载器)的代码在更广泛的父命名空间(父类加载器)中不可用(可见);并且没有什么好的方法可以侵入这个系统。
幸运的是,您可以定义父命名空间中可用的接口,然后在仅在子命名空间中可见的代码中实现这些接口。然后,仅位于父命名空间内的代码能够使用此接口名称进行转换和转换。方法调用的目的,从而访问您的子命名空间组件。
为了实现这一点,您必须确保您的自定义类加载器不仅加载父加载器无法访问的组件(即位于其类路径之外的组件),还加载您的代码的那些部分它直接与这些组件交互并显式引用这些符号(类型名称/方法等)。否则,对这些组件的引用最终会出现在父命名空间中(请记住,引导类加载器默认加载您自己的所有代码),并且您将回到原来的问题。
您可以通过颠覆预期的类加载委托模型来做到这一点。通常,在尝试自己加载类之前,您会遵循父加载器的指示。但是,现在您必须提前检查您的代码是否不会触及父加载程序无法使用的任何这些组件。最简单的方法可能是设置代码路径,使类加载器维护一组类名,这些类名将自行加载,而不是允许父加载器加载它们。
您必须找到一种方法以某种方式告诉您的自定义类加载器,可以使用类声明上的类型注释来实现此目的。这里的想法是,您的类加载器内省由父加载器加载的类,如果它在类型名称上找到特定的自定义注释,它将调用注释上的方法来获取类符号的名称,并且不允许其父加载器访问该类符号。加载。
示例:
请注意,由于
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:
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:
Note that because the class
MyFeatureProvider
will be loaded beforeMyImpl
is, your classloader will know in advance about the annotation inMyFeatureProvider
so it will be able to override the default delegation model for MyImpl. Because the rest of your code only interacts withMyImpl
as an instance ofMyFeature
the parent loader never need to balk at the sight of undefined symbols -- and the ClassNoDefFound error is solved.