java Jakarta WebSocket handler with Spring managed bean

k4ymrczo  于 2023-04-04  发布在  Java
关注(0)|答案(1)|浏览(121)

我有以下用于对等通信的WebSocket客户端和服务器:

package network;

import jakarta.websocket.ContainerProvider;
import jakarta.websocket.Session;
import jakarta.websocket.WebSocketContainer;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;

@Service
public class WebsocketNetwork {

    private final MessageHandler messageHandler;

    @Bean
    public WebSocketClient webSocketClient() {
        return new StandardWebSocketClient();
    }

    @Autowired
    public WebsocketNetwork(MessageHandler messageHandler) {

        this.messageHandler = messageHandler;

    }

    @EventListener(value = ApplicationReadyEvent.class)
    public void establishConnection() {

        WebSocketContainer container = ContainerProvider.getWebSocketContainer();

        URI address = new URI("ws://localhost:15000/websocket");

        Session session = container.connectToServer(this.messageHandler, address);
    }

}

经办人:

package network;

import jakarta.websocket.ClientEndpoint;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
@ServerEndpoint(value = "/websocket", decoders = MessageDecoder.class, encoders = MessageEncoder.class)
@ClientEndpoint(decoders = MessageDecoder.class, encoders = MessageEncoder.class)
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class MessageHandler {

    // MessageHandler should have no argument constructor else it fails with:
    /*

    Caused by: java.lang.NoSuchMethodException: network.MessageHandler.<init>()
        at java.base/java.lang.Class.getConstructor0(Class.java:3641) ~[na:na]
        at java.base/java.lang.Class.getConstructor(Class.java:2324) ~[na:na]
        at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:135) ~[tomcat-embed-core-10.1.7.jar:10.1.7]
        at org.apache.tomcat.websocket.WsSession.<init>(WsSession.java:275) ~[tomcat-embed-websocket-10.1.7.jar:10.1.7]

    */

    @Autowired
    private ApplicationEventPublisher eventPublisher;       // <-- why this Null? And how to fix it?

    @OnClose
    public void onClose(Session session) {
        System.out.println("Disconnected from server: " + session.getId());
    }

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("Connected to server: " + session.getId());
    }

    // incoming messages
    @OnMessage
    public void receive(NetworkMessage message) {

        // handle incoming message
        
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        // handle error
        System.err.println(throwable.getMessage());
    }

}

在上面的处理程序中,问题是ApplicationEventPublishernull,即使它是Autowired,当我调试它时,WebSocket容器似乎正在创建一个newInstance,而没有考虑它的依赖关系,在这里,有人知道如何修复它吗?

bvhaajcl

bvhaajcl1#

要使用Spring-managed bean作为WebSocket处理程序,您可以定义一个自定义的javax.websocket.server.ServerEndpointConfig.Configurator类并覆盖其getEndpointInstance()方法以返回Spring-managed bean示例。

import javax.websocket.server.ServerEndpointConfig;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class WebSocketConfigurator extends ServerEndpointConfig.Configurator {

    private static ApplicationContext applicationContext;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketConfigurator.applicationContext = applicationContext;
    }

    @Override
    public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
        return applicationContext.getBean(endpointClass);
    }
}

要在Jakarta WebSocket应用程序中使用此Configurator类,您可以将以下注解添加到WebSocket端点类:

import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/websocket", configurator = WebSocketConfigurator.class)
public class MessageHandler {
    // ...
}

相关问题