spring boot websocket 實時消息推送和數(shù)據(jù)展示

一、實現(xiàn)目標(biāo)

  • 1、在線聊天,客服聊天,聊天室
  • 2、業(yè)務(wù)數(shù)據(jù)實時展示,自動更新

二、實踐步驟

以下是為了實現(xiàn):業(yè)務(wù)數(shù)據(jù)實時展示,自動更新

1. Maven引入

<dependency>  
   <groupId>org.springframework.boot</groupId>  
   <artifactId>spring-boot-starter-websocket</artifactId>  
</dependency> 

2. 創(chuàng)建配置文件

WebSocketConfig

/**
 * 開啟WebSocket支持
 * @author zhengkai
 */
@Configuration
public class WebSocketConfig {

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

3. 創(chuàng)建服務(wù)端

@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    //concurrent包的線程安全Set,用來存放每個客戶端對應(yīng)的MyWebSocket對象。
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();

    //與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
    private Session session;

    /**
     * 連接建立成功調(diào)用的方法
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);
        sendMessage("連接成功");
    }

    /**
     * 連接關(guān)閉調(diào)用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        log.info("有一連接關(guān)閉!");
    }

    /**
     * 收到客戶端消息后調(diào)用的方法
     *
     * @param queryType 客戶端發(fā)送過來的消息
     */
    @OnMessage
    public void onMessage(String queryType) {
        log.info("收到信息:" + queryType);

        //群發(fā)消息
        for (WebSocketServer item : webSocketSet) {
            item.sendMessage("hello");
        }
    }

    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("發(fā)生錯誤");
        error.printStackTrace();
    }

    /**
     * 實現(xiàn)服務(wù)器主動推送
     */
    public void sendMessage(Object message) {
        try {
            this.session.getBasicRemote().sendText(JSON.toJSONString(message));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 實現(xiàn)服務(wù)器主動群發(fā)消息
     */
    public static void sendInfo(Object message) {
        for (WebSocketServer item : webSocketSet) {
            item.sendMessage(message);
        }
    }
}

4. 前端發(fā)起websocket請求

  <script> 
    var socket;  
    if(typeof(WebSocket) == "undefined") {  
        console.log("您的瀏覽器不支持WebSocket");  
    }else{  
        console.log("您的瀏覽器支持WebSocket");  
            //實現(xiàn)化WebSocket對象,指定要連接的服務(wù)器地址與端口  建立連接  
            socket = new WebSocket("ws://localhost:8080/webscoket");
            //打開事件  
            socket.onopen = function() {  
                console.log("Socket 已打開");  
                //socket.send("這是來自客戶端的消息" + location.href + new Date());  
            };  
            //獲得消息事件  
            socket.onmessage = function(msg) {  
                console.log(msg.data);  
                //發(fā)現(xiàn)消息進(jìn)入    開始處理前端觸發(fā)邏輯
            };  
            //關(guān)閉事件  
            socket.onclose = function() {  
                console.log("Socket已關(guān)閉");  
            };  
            //發(fā)生了錯誤事件  
            socket.onerror = function() {  
                alert("Socket發(fā)生了錯誤");  
                //此時可以嘗試刷新頁面
            } 
    }
    </script> 

三、實踐中會遇到的問題

1. 前端發(fā)起websocket請求, 卻無法連接websocket

  • 錯誤一:被權(quán)限攔截,例如spring security
    解決方式:放開權(quán)限或允許訪問
// spring security
.antMatchers(
    "/websocket"
    )
.permitAll()

  • 錯誤二:請求的websocket地址錯誤
@ServerEndpoint(value = "/websocket")

對應(yīng)的地址是:ws://localhost:8080/webscoket
  • 錯誤三:被AOP攔截
@Pointcut("execution(public * com.xxx.xxx.controller..*(..))")
public void webLog() {
}

解決辦法

// 排除攔截
@Pointcut("execution(public * com.xxx.xxx.controller..*(..)) && !execution(public * com.xxx.xxx.controller.xxx*(..))")
public void webLog() {
}

2. 在WebSocketServer中,無法注入Bean,無法使用@Autowired

即你按如下方式直接使用@Autowired,會報錯

@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer {
    @Autowired
    ChartDataService chartDataService;
}

在WebSocket中, 因 SpringBoot+WebSocket 對每個客戶端連接都會創(chuàng)建一個 WebSocketServer(@ServerEndpoint 注解對應(yīng)的) 對象,Bean 注入操作會被直接略過,因而手動注入一個全局變量(即你需要注入的bean)

@Configuration
public class WebSocketConfig {

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

    // 在這里注入你需要的bean
    @Autowired
    public void setMessageService(ChartDataService chartDataService) {
        WebSocketServer.chartDataService = chartDataService;
        // ...
    }
}

使用你注入的bean

@ServerEndpoint(value = "/websocket")
@Component
public class WebSocketServer {

    public static ChartDataService chartDataService;

此方式經(jīng)實踐,有效

其他解決方式: https://stackoverflow.com/questions/29306854/serverendpoint-and-autowired

  • 1.使用@Inject
@ServerEndpoint("/ws")
public class MyWebSocket {   
    @Inject
    private ObjectMapper objectMapper;
}
  • 2.使用SpringConfigurator
@ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)
  • 3.使用@PostConstruct
@PostConstruct
public void init(){
    SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}

經(jīng)過實踐,上述三種方式,對于我而言,均無效

參考鏈接:https://blog.csdn.net/moshowgame/article/details/80275084

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容