如何构建同一个加载不同类加载器的两个实例?

我有两个不同的webapps,每个都加载相同的A类和不同的类加载器。 当我在会话中放入一个实例然后从另一个webapp获取它时, ClassCastException

例如,在webapp A中,我在会话中存储a ,然后在webapp B中,我从会话中获取a并将其转换为A, ClassCastException

有办法解决这个问题吗?

有办法解决这个问题吗?

基本上没有。

就JLS而言,类型是不同的类型,并且JVM不允许您以其他方式伪装。 例如,类可以具有不同的代码和不同的对象布局。 如果您可以欺骗JVM将类型视为相同类型,那么您将能够吹走JVM运行时的安全性。 那种方式就是精神错乱。

解决方案是确保您没有两个不同的类加载器加载相同的类。 在Tomcat的上下文中,这意味着如果两个或更多webapps需要共享一个类的实例,那么该类必须在两个共同的类加载器中定义; 例如,将JAR文件放在$CATALINA_HOME/lib$CATALINA_HOME/common目录中。


如果有一个合理的原因,为什么类必须由不同的类加载器加载(可能因为类真的不同),那么你可以通过定义类的两个版本实现的接口,然后编程到接口来解决问题而不是实现类。 当然,只能加载一个版本的接口…否则你会再次遇到同样的问题。

您应该避免这种情况,基本上 – 要么将两个function位置放在同一个webapp中,要么将包含A类的库移动到适当的位置,这样只使用一个类加载器。 不同类加载器加载的两个类在JVM中完全不同 – 您根本无法在它们之间进行转换。

有关使用的各种类加载器的更多详细信息,请参阅Tomcat类加载器文档 。 看起来你想把这个普通的类放到公共的类加载器区域。 正如文档所述,这是非常不寻常的,但如果你真的想在两个webapps之间共享一个对象(这很不寻常),那么这可能是最简单的方法。

你不能。 由不同类加载器加载的两个类是不同的。

也许你可以序列化共享对象?

我不一定提倡这种方法,但是这又是序列化本质上做的 – 你从一些JVM或ClassLoader X序列化并将其加载(反序列化)到另一个JVM / ClassLoader Y

即使类具有相同的包名和签名,也不能从不同的类中转换两个对象,但是只需使用apache bean utils库, BeanUtils.copyProperties(o1, o2);就可以将数据从一个复制到另一个BeanUtils.copyProperties(o1, o2);

这可以通过解决方法实现。

虽然你不能将一个对象从类加载器A加载的一个类转换为类加载器B加载的同一个类(如果在不同的类加载器下加载,如同在这里解释的那样具有相同名称的类不兼容),则在webapp容器中例如Jetty或Tomcat,您可以在父类加载器中加载该类一次,这将由JVM中的所有Web应用程序使用。 每个webapp类加载器都将遵循类定义的共享(父)类加载器,并且来回转换它可以正常工作。

例如,使用Jetty,请使用此处所述的WebAppContext.addSystemClass()