首页 > 解决方案 > Could not deserialize result from HTTP invoker remote service [...]; nested exception is java.lang.ClassNotFoundException:

问题描述

I am receiving the following error only when a shared library is added to my application on an IBM WebSphere instance (version 8.5.xx). The goal is to move a lot of shared libraries (jars) between some applications to a shared library on the server in order to reduce the war sizes of the applications. From what I have seen though, it doesn't matter if the war file size is reduced or not, whenever this shared library is added, we are seeing the below errors.

org.springframework.remoting.RemoteAccessException: Could not deserialize result from HTTP invoker remote service [<URL>]; nested exception is java.lang.ClassNotFoundException: com.example.models.ExampleModel
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.convertHttpInvokerAccessException(HttpInvokerClientInterceptor.java:221)
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.invoke(HttpInvokerClientInterceptor.java:153)
    at com.example.configuration.LoggingHttpInvokerProxyFactoryBean.invoke(LoggingHttpInvokerProxyFactoryBean.java:28)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy305.getSomeListOfObjects(Unknown Source)
...
...
...
Caused by: java.lang.ClassNotFoundException: com.example.models.ExampleModel
    at java.lang.Class.forNameImpl(Native Method)
    at java.lang.Class.forName(Class.java:403)
    at java.io.ClassCache$FutureValue.get(ClassCache.java:177)
    at java.io.ClassCache.get(ClassCache.java:148)
    at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:834)
    at org.springframework.core.ConfigurableObjectInputStream.resolveClass(ConfigurableObjectInputStream.java:78)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2017)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1900)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2194)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1722)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2439)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:2363)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2221)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1722)
    at java.io.ObjectInputStream.readObjectImpl(ObjectInputStream.java:540)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:475)
    at org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor.doReadRemoteInvocationResult(AbstractHttpInvokerRequestExecutor.java:291)
    at org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor.readRemoteInvocationResult(AbstractHttpInvokerRequestExecutor.java:242)
    at org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor.doExecuteRequest(HttpComponentsHttpInvokerRequestExecutor.java:248)
    at org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor.executeRequest(AbstractHttpInvokerRequestExecutor.java:137)
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.executeRequest(HttpInvokerClientInterceptor.java:202)
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.executeRequest(HttpInvokerClientInterceptor.java:184)
    at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.invoke(HttpInvokerClientInterceptor.java:150)

The following is a similar example of what the model class looks like on a smaller scale. I primarily wanted to note that it does indeed implement Serializable (using the default serialVersionUID):

package com.example.models.ExampleModel;

public class ExampleModel implements Serializable {
    private static final long serialVersionUID = 1L;
    private String customerNumber = "";
}

Any debugging ideas even would be greatly appreciated at a minimum. I also wanted to note, that calling this remoting method works fine when the shared library isn't added to the application on the server, but doesn't seem to work when the shared library is added. Also, the shared library on the server does not have the jar that contains the class that cannot be found. The jar with the class that cannot be found is in my WEB-INF/lib folder and I have verified that to be installed on the server.

标签: javaspringwebspheredeserializationspring-remoting

解决方案


这看起来像是类加载器层次结构的问题,其中由应用程序 (EAR) 类加载器加载的类正在尝试加载打包在 Web 模块 (WAR) 加载器中的类。我相信 ObjectInputStream 的 resolveClass() 方法正在使用其调用者的类加载器来加载失败的类,并且我在这里还假设您的 Spring 类在共享库中。

在 Java EE 应用程序中,EAR 和 WAR 具有单独的类加载器,EAR 的加载器充当父类。当共享库与 WebSphere 中的应用程序相关联时,其内容将添加到 EAR 加载器的类路径中。如果共享库中的类需要对 WAR 中打包的类可见,这是一个问题,因为父加载器无法“看到”它们的子加载器。WebSphere 允许您将共享库与应用程序或应用程序中的一个或多个 Web 模块相关联,这会更改将库的类路径附加到哪个类加载器。

假设我正确理解了 resolveClass 的打包和行为,我认为解决方案应该相当简单 - 与其将共享库与应用程序相关联,不如将其与包含未找到的类的 Web 模块相关联(请注意,您还需要删除 EAR 级别的关联,否则它只会从那里加载 Spring 类,并且会出现同样的问题)。这将使 WAR 路径和共享库路径都进入同一个类加载器,从而消除层次结构问题。

请注意,即使我的假设是正确的,如果您为共享库选择了“使用隔离的类加载器”选项,该解决方案也将不起作用 - 该设置会创建一个单独的类加载器,该类加载器作为它与之关联的 WAR/EAR,因此即使它与 WAR 正确关联,它仍然无法看到 WAR 中的类。如果存在需要隔离类加载行为的 jar,则必须将 WAR 类重新定位到共享库或将 Spring 内容放回 WAR。


推荐阅读