无法反序列化来自 HTTP 调用程序远程服务的结果 [...];嵌套异常是 java.lang.ClassNotFoundException:
Could not deserialize result from HTTP invoker remote service [...]; nested exception is java.lang.ClassNotFoundException:
仅当将共享库添加到我在 IBM WebSphere 实例(版本 8.5.xx)上的应用程序时,我才收到以下错误。目标是将一些应用程序之间的大量共享库 (jars) 移动到服务器上的共享库,以减少应用程序的 war 大小。从我所看到的情况来看,war 文件大小是否减小并不重要,无论何时添加此共享库,我们都会看到以下错误。
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)
以下是模型 class 在较小比例下的类似示例。我主要想指出它确实实现了 Serializable(使用默认的 serialVersionUID):
package com.example.models.ExampleModel;
public class ExampleModel implements Serializable {
private static final long serialVersionUID = 1L;
private String customerNumber = "";
}
任何调试想法至少都会非常感激。
我还想指出,当共享库未添加到服务器上的应用程序时,调用此远程方法可以正常工作,但在添加共享库时似乎不起作用。另外,服务器上的共享库没有包含找不到的class的jar。无法找到带有 class 的 jar 在我的 WEB-INF/lib 文件夹中,我已验证将其安装在服务器上。
这看起来像是 class 加载器层次结构的问题,其中 class 由应用程序 (EAR) 加载 class 加载器正在尝试加载 classes打包在 web 模块 (WAR) 加载器中。我相信 ObjectInputStream 的 resolveClass() 方法正在使用其调用者的 class 加载程序来处理失败的 class 加载,并且我在这里还假设您的 Spring classes 在其中共享库中的那些。
在 Java EE 应用程序中,EAR 和 WAR 具有单独的 class 加载器,EAR 的加载器作为父加载器。当共享库与 WebSphere 中的应用程序相关联时,其内容将添加到 EAR 加载程序的 class 路径。如果共享库中的 classes 需要对打包在 WAR 中的 classes 可见,这是一个问题,因为父加载器无法 "see" 它们的子加载器。 WebSphere 允许您将共享库与应用程序或应用程序中的一个或多个 Web 模块相关联,这会更改库的 class 路径附加到哪个 class 加载器。
假设我正确理解了 resolveClass 的打包和行为,我认为解决方案应该相当简单——不是将共享库与应用程序相关联,而是将它与包含 class 没有找到(请注意,您还需要删除 EAR 级关联,否则它只会从那里加载 Spring classes 并且同样的问题会发生)。这将使 WAR 路径和共享库路径都进入同一个 class 加载程序,从而消除层次结构问题。
请注意,即使我的假设是正确的,如果您为共享库选择了 "use an isolated class loader" 选项,该解决方案也不会起作用 - 该设置会创建一个单独的 class 加载程序,作为它所关联的 WAR/EAR 的父级排序,因此它仍然无法在 WAR 中看到 classes,即使它与 WAR 适当关联.如果有需要隔离 class 加载行为的 jar,您将不得不将 WAR classes 重新定位到共享库或将 Spring 东西放回WAR.
@Jarid 提供了详细的解决方案。如果您寻求其他调试步骤,请阅读他的解决方案。
简而言之,我有一个共享库,其中包含附加到应用程序和 Web 模块的 Spring 框架 jar。从应用程序中删除共享库并仅附加到 Web 模块后,应用程序开始正常工作并能够反序列化结果。
仅当将共享库添加到我在 IBM WebSphere 实例(版本 8.5.xx)上的应用程序时,我才收到以下错误。目标是将一些应用程序之间的大量共享库 (jars) 移动到服务器上的共享库,以减少应用程序的 war 大小。从我所看到的情况来看,war 文件大小是否减小并不重要,无论何时添加此共享库,我们都会看到以下错误。
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)
以下是模型 class 在较小比例下的类似示例。我主要想指出它确实实现了 Serializable(使用默认的 serialVersionUID):
package com.example.models.ExampleModel;
public class ExampleModel implements Serializable {
private static final long serialVersionUID = 1L;
private String customerNumber = "";
}
任何调试想法至少都会非常感激。 我还想指出,当共享库未添加到服务器上的应用程序时,调用此远程方法可以正常工作,但在添加共享库时似乎不起作用。另外,服务器上的共享库没有包含找不到的class的jar。无法找到带有 class 的 jar 在我的 WEB-INF/lib 文件夹中,我已验证将其安装在服务器上。
这看起来像是 class 加载器层次结构的问题,其中 class 由应用程序 (EAR) 加载 class 加载器正在尝试加载 classes打包在 web 模块 (WAR) 加载器中。我相信 ObjectInputStream 的 resolveClass() 方法正在使用其调用者的 class 加载程序来处理失败的 class 加载,并且我在这里还假设您的 Spring classes 在其中共享库中的那些。
在 Java EE 应用程序中,EAR 和 WAR 具有单独的 class 加载器,EAR 的加载器作为父加载器。当共享库与 WebSphere 中的应用程序相关联时,其内容将添加到 EAR 加载程序的 class 路径。如果共享库中的 classes 需要对打包在 WAR 中的 classes 可见,这是一个问题,因为父加载器无法 "see" 它们的子加载器。 WebSphere 允许您将共享库与应用程序或应用程序中的一个或多个 Web 模块相关联,这会更改库的 class 路径附加到哪个 class 加载器。
假设我正确理解了 resolveClass 的打包和行为,我认为解决方案应该相当简单——不是将共享库与应用程序相关联,而是将它与包含 class 没有找到(请注意,您还需要删除 EAR 级关联,否则它只会从那里加载 Spring classes 并且同样的问题会发生)。这将使 WAR 路径和共享库路径都进入同一个 class 加载程序,从而消除层次结构问题。
请注意,即使我的假设是正确的,如果您为共享库选择了 "use an isolated class loader" 选项,该解决方案也不会起作用 - 该设置会创建一个单独的 class 加载程序,作为它所关联的 WAR/EAR 的父级排序,因此它仍然无法在 WAR 中看到 classes,即使它与 WAR 适当关联.如果有需要隔离 class 加载行为的 jar,您将不得不将 WAR classes 重新定位到共享库或将 Spring 东西放回WAR.
@Jarid 提供了详细的解决方案。如果您寻求其他调试步骤,请阅读他的解决方案。
简而言之,我有一个共享库,其中包含附加到应用程序和 Web 模块的 Spring 框架 jar。从应用程序中删除共享库并仅附加到 Web 模块后,应用程序开始正常工作并能够反序列化结果。