Spring Boot Websocket部署異常處理:Multiple Endpoints may not be deployed to the same path

spring boot websocket 配置

開啓websocket

@Confituration
public class MyConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

配置websocket訪問處理

@ServerEndpoint(value = "/websocket/myurl"
@Component
public class WebSocketServer {
    @OnOpen
    public void open(Session session) {
    }
    
    @OnClose
    public void onClose() {
    
    }
    
    @OnMesage
    public void onMessage(String message, Session session){
    }
    
    @OnError
    public void onError(Session session, Throwable error){
    }
}

部署問題描述

在開發IDE的環境下,進行開發調試,沒有任何問題。
如果工程打包方式爲war,且部署到tomcat下,會產生問題。

...
Caused by: java.lang.IllegalStateException: Failed to register @ServerEndpoint class: class org.yunzhong.websocket.WebSocketServer
...
Caused by: javax.websocket.DeploymentException: Multiple Endpoints may not be deployed to the same path [/websocket/myurl] : existing endpoint was [class org.yunzhong.websocket.WebSocketServer] and new endpoint is [class org.yunzhong.websocket.WebSocketServer]

從異常信息可以看出,是我們寫的WebSocketServer被註冊了兩次。
也就是說tomcat自己也註冊了一次。獨立部署的tomcat和embedded tomcat 有何區別,這裏有待查看文檔。

方案

網上的基本都是在發佈的時候註釋掉ServerEndpointExporter Bean的初始化。但是對於真實的開發場景來說,這個方案只能是臨時方案。
根據Spring的配置理念,應該可以通過條件來判斷是否初始化bean。只需要判斷當前的運行環境爲Embedded還是獨立部署,就能動態判斷加載是否創建ServerEndpointExporter。
我們先來看EmbeddedTomecat初始化的代碼:

...
public class EmbeddedServletContainerAutoConfiguration {
    @Configuration
    @ConditionalOnClass({Servlet.class, Tomcat.class})
    @ConditionalOnMissingBean(value =EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT )
    public static class EmbeddedTomcat {
        
        @Bean
        public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory(){
            return new TomcatEmbeddedServletContainerFactory();
        }
    }
}

從條件可以看出,當EmbeddedServletContainerFactory有實例化的bean定義時,纔會初始化TomcatEmbeddedServletContainerFactory。
以此類推,我們在ServerEndpointExporter上添加bean依賴聲明:當 TomcatEmbeddedServletContainerFactory 有實例的時候,才創建ServerEndpointExporter。

@Confituration
public class MyConfig {
    @Bean
    @ConditionalOnBean(TomcatEmbeddedServletContainerFactory.class)
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章