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上。
