網頁

2019/4/27

Spring Boot WebSocket SimpMessageSendingOperations的bean無法注入

今日在練習使用Spring Boot WebSocket建立一個簡單的聊天室網頁時,在啟動時出現SimpMessageSendingOperations的bean無法注入的錯誤如下。

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-04-27 17:06:38.044 ERROR 8864 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field messagingTemplate in idv.matt.demo.listener.WebSocketEventListener required a bean of type 'org.springframework.messaging.simp.SimpMessageSendingOperations' that could not be found.

The injection point has the following annotations:
 - @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'org.springframework.messaging.simp.SimpMessageSendingOperations' in your configuration.

原因是我在一個自訂用來監聽WebSocket事件的WebSocketEventListener類別中使用@Autowired注入SimpMessageSendingOperations的實例時,在Spring Boot啟動時發現找不到可注入的bean的情況而造成此錯誤。

WebSocketEventListener

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.messaging.SessionConnectedEvent;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

@Component
public class WebSocketEventListener {
    
    @Autowired
    private SimpMessageSendingOperations messagingTemplate; // 注入時出現錯誤

    @EventListener
    public void handleWebSocketConnectListener(SessionConnectedEvent event) {
        ...
    }
    
    @EventListener
    public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
        ...
    }
}

最後發現是Spring Boot啟動時並沒有對掛有@EnableWebSocketMessageBroker並實作WebSocketMessageBrokerConfigurer介面的WebSocketConfig類別進行掃描(此類別負責配置STOMP的訊息代理及WebSocket的end point)。因為我把@Configuration錯寫成@Configurable,所以Spring Boot啟動過程掃描要註冊為bean的類別時忽略了此類別,所以在WebSocketEventListener要注入成員變數SimpMessageSendingOperations的bean時發生上述找不到bean的錯誤。

WebSocketConfig

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration // 原本錯寫成@Configurable,導致Spring Boot不掃描此配置類別
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
   
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        ...
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        ... 
    }

}


TL;DR



SimpMessageSendingOperations的實例SimpMessagingTemplate是在AbstractMessageBrokerConfiguration.brokerMessagingTemplate()中產生。

@EnableWebSocketMessageBroker實際上匯入了DelegatingWebSocketMessageBrokerConfiguration的配置,
DelegatingWebSocketMessageBrokerConfiguration繼承了WebSocketMessageBrokerConfigurationSupport
WebSocketMessageBrokerConfigurationSupport繼承了AbstractMessageBrokerConfiguration
AbstractMessageBrokerConfiguration.brokerMessagingTemplate()則是一個掛有@Bean,用來宣告bean的方法。

AbstractMessageBrokerConfiguration

public abstract class AbstractMessageBrokerConfiguration implements ApplicationContextAware {
    ...
    @Bean
    public SimpMessagingTemplate brokerMessagingTemplate() {
        SimpMessagingTemplate template = new SimpMessagingTemplate(brokerChannel());
        String prefix = getBrokerRegistry().getUserDestinationPrefix();
        if (prefix != null) {
            template.setUserDestinationPrefix(prefix);
        }
        template.setMessageConverter(brokerMessageConverter());
        return template;
    }
    ...
}

參考:

沒有留言:

張貼留言