我有两个 Eclipse 项目(在同一个工作区中),一个是运行码头实例的标准 java 项目。另一个是 GWT 项目。GWT 项目(在文件系统上)作为 web 应用程序存在于 java 项目中,并由 jetty (xml) 配置加载。
我在调试时都通过 eclipse 运行,首先我将 jetty 服务器作为常规 java 应用程序(localhost:8080)启动,然后将 GWT 项目作为“Web 应用程序(在外部服务器上)”启动。
一切都很好,除了尝试投射课程时。在某些情况下,我会得到 java.lang.ClassCastException。经过数小时的调试,我确定这是由于使用了两个单独的类加载器,每个类加载器一个。
我认为这源于在 servlet 上下文中创建的对象和不是在 servlet 上下文中创建的对象。
我创建了一个测试来证明这一点。假设从传入请求到 servlet 调用以下方法,参数 instanceOfClassA 在 servlet 请求处理程序的某处创建并传递给此方法:
public void calledFromServlet(ClassA instanceOfClassA){
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// systemClassLoader is sun.misc.Launcher$AppClassLoader@45a877
Object classA1 = new ClassA();
ClassLoader loader1 = classA1.getClass().getClassLoader();
// loader1 is is WebAppClassLoader=GWTProject@b98fe1
ClassLoader loader2 = instanceOfClassA.getClass().getClassLoader();
// loader2 is sun.misc.Launcher$AppClassLoader@45a877
ClientA request = (ClassA)instanceofClassA; // java.lang.ClassCastException!
}
如您所见(希望),在当前上下文中创建的对象由 WebAppClassLoader=GWTProject@b98fe1 加载,在 servlet 上下文中创建的对象(在其他地方)由系统加载程序 sun.misc.Launcher$AppClassLoader@45a877 加载。
因此,当我尝试将在 servlet 上下文中创建的对象强制转换为当前上下文中的同一个类时,我得到了 ClassCastException。
不用说这真的毁了我的一周,我无法解决这个问题,因为我需要能够投射物体。
有没有人经历过这种情况并有解决方案?
更新(1 月 5 日凌晨 2:24):我尝试使用 Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader()) 强制当前线程的类加载器。还是不行。
更新(1 月 6 日下午 1:09——Thomas Boyer)
似乎有两个感兴趣的线程正在运行,一个我的断点位于:
Thread [38] (Suspended (breakpoint at line 53 in CollaborationSocketListener))
CollaborationSocketListener.onMessage(String) line: 53
WebSocketConnectionRFC6455$WSFrameHandler.onFrame(byte, byte, Buffer) line: 845
WebSocketParserRFC6455.parseNext() line: 359
WebSocketServletConnectionRFC6455(WebSocketConnectionRFC6455).handle() line: 235
SelectChannelEndPoint.handle() line: 606
SelectChannelEndPoint$1.run() line: 46
QueuedThreadPool.runJob(Runnable) line: 603
QueuedThreadPool$3.run() line: 538
Thread.run() line: 662
另一个属于 gwt-dev 启动器:
com.google.gwt.dev.DevMode at localhost:37240
Thread [main] (Running)
Thread [Thread-0] (Running)
Daemon Thread [Thread-1] (Running)
Daemon Thread [UnitWriteThread] (Running)
Daemon Thread [Timer-0] (Running)
Daemon Thread [Code server listener] (Running)
Daemon Thread [com.google.gwt.thirdparty.guava.common.base.internal.Finalizer] (Running)
Daemon Thread [com.google.inject.internal.util.$Finalizer] (Running)
Daemon Thread [Code server for freshgwtp from Mozilla/5.0 (X11; Linux i686) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.47 Safari/536.11 on http://localhost:8080/FreshGWTP.html?gwt.codesvr=127.0.0.1:9997 @ h`"*x.nukanE_ke_] (Running)
被调用的方法是为了响应传入的消息到 servlet (WebSocket servelet)。相关代码在这里:
public void onMessage(String serRequest) {
// Create a request here to show that we can cast anything created in this context
Object uFakeRequest = new ClientConnectRequest("someSite", new Client("SomeClient"));
// This cast works fine
ClientConnectRequest fakeRequest = (ClientConnectRequest)uFakeRequest;
// Now I build the same object from a serialized object using a "deserializer" instantiated elsewhere (Note what it is instantiated does not seem to make a difference
Object uRequest = streamer.fromString(serRequest);
// ClassCastException
ClientConnectRequest request = (ClientConnectRequest)uRequest;
CollaborationSite site = siteManager.getOrCreateSite(request.getSiteID());
site.onRequestRecieved(con, request);
}
“streamer”是一个 gwt-streamer(有关详细信息,请参阅 google 代码),我尝试在 servelet init() 中实例化它,以及在此方法中内联,并通过 Guice 注入。我总是得到相同的类转换异常。
原因是streamer的类加载器是系统类加载器,而当前方法中的类加载器是GWT-Dev类加载器。
** 更新(1 月 5 日下午 2:36)更多混乱 **
为了分离类加载器之间的冲突,我启动了两个单独的 eclipse 实例,一个启动了 jetty 应用程序,另一个启动了 gwt-dev 模式(在外部服务器上)。仍然得到同样的错误。
我不明白为什么 GWT 认为应该在外部服务器的服务器端代码上处理类加载......