1、場景
在用websocket做一個簡單的資料匯入頁面同步顯示後臺進度功能的時候,瀏覽器出現連線不上的錯誤:
WebSocket connection to 'ws://localhost:8080/project/marco' failed: Error during WebSocket handshake: Unexpected response code: 404
網上有很種導致404的情況,但是都沒有用,我在這裡記錄一下我的情況。
2、解決過程
spring-websocket.xml的配置:
<websocket:handlers>
<websocket:mapping handler="messageHandler" path="/marco" />
<websocket:handshake-interceptors>
<bean class="com.project.websocket.HandshakeInterceptor"/>
</websocket:handshake-interceptors>
</websocket:handlers>
<bean id="messageHandler" class="com.project.websocket.MessageHandler"/>
檢視列印的INFO級別日誌,根據spring的啟動資訊我們可以看到:
2017-02-09 21:57:40,559 INFO [springframework.web.servlet.handler.SimpleUrlHandlerMapping] - Mapped URL path [/marco] onto handler of type [class org.springframework.web.socket.server.support.WebSocketHttpRequestHandler]
WebSocketHttpRequestHandler通過SimpleUrlHandlerMapping註冊了/marco地址,那應該是沒錯的啊,難道是客戶端js的錯誤?
var websocket = new WebSocket('ws://' + window.location.host + '/project/marco');
地址好像也沒錯啊,和Spring in Action4裡面的例子一樣的啊。
為什麼地址沒有對映到/marco?先看看DispatchServlet的handlerMappings有沒有/marco先吧。
斷點DispatchServlet的doDispatch()方法,隨便發起一個請求:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
......
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
......
}
進入getHandler(processedRequest)
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
我們看一下handlerMappings裡面的內容,不知道你們有沒有找到/marco
,反正我是沒找到。
言下之意,/marco沒註冊到?
沒辦法,我們斷點SimpleUrlHandlerMapping的registerHandlers()方法和DispatchServlet的initHandlerMappings()方法。
我們看到SimpleUrlHandlerMapping的registerHandlers()先被執行。
最後退出registerHandlers()方法,F6一路下來,從WebApplicationObjectSupport的initApplicationContext()方法中可以看到:
- context.beanFactory.beanDefintionMap中包含
[org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#0->RootBeanDefintion]
。 - 而且context.beanFactory.singletonObjects也包含了註冊了
/marco
的SimpleUrlHandlerMapping例項。 - 此時的context.configLocations是
classpath:spring-context.xml
。
DispatchServlet的initHandlerMappings()後被執行,我們來看這段:
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
然而,在matchingBeans沒有找到我們要的註冊了/marco
的SimpleUrlHandlerMapping。此時的context.configLocations是classpath*:spring-servlet.xml
來到這裡,你大概知道就是你的spring-websocket.xml放錯位置啦。
3、解決辦法
如果你的web.xml分開載入spring-context和spring-servlet的話,請將spring-websocket的資原始檔放到spring-servlet上載入。
注意:在spring-context載入的時候,你的messageHandler應該是還沒有初始化,請不要注入到某個service上。