-->

Integrating JSR-356 WebSocket @ServerEndpoint with

2019-01-26 11:42发布

问题:

I'm using Spring 3.2.5 without full new JSR-356 WebSockets support.

I would like to have singleton-bean reference in my @ServerEndpoint WebSocket server, which is instantiated by servlet container itself, not in Spring context.

What is the clean way to do it?

My current solution: I made @Service singleton bean with instance in static field:

@Service
public class WebSocketSupportBean {
    private volatile static WebSocketSupportBean instance = null;

    public static WebSocketSupportBean getInstance() {
        return instance;
    }

    public WebSocketSupportBean() {
        instance = this;
    }

and just getting it in @ServerEndpoint by static method, disconnecting user if null returned (if bean not jet created during server startup but user connects):

回答1:

You can setup websockets with spring framework 3.x

I developed a small proof-of-concept application to demonstrate how, based on Rossen Stoyanchev's SpringConfiguration released with spring-core 4.0.

The application sets up a websocket server endpoint with uri /wstest which will use a @Autowired spring bean to select a greeting word and reply to a websocket message.

The websocket connection is initiated and messages sent by an html page (index.html) running in a browser that supports websockets.

The Endpoint registration is made by a ServletContextListener at context initialization and when the endpoint is instantiated it will be wired with spring:

@WebListener
public class MyApplication implements ServletContextListener {

    private final static String SERVER_CONTAINER_ATTRIBUTE = "javax.websocket.server.ServerContainer";

    @Override
    public void contextInitialized(ServletContextEvent sce) {

        ServletContext container = sce.getServletContext();

        final ServerContainer serverContainer = (ServerContainer) container.getAttribute(SERVER_CONTAINER_ATTRIBUTE);
        try {
            serverContainer.addEndpoint(new MyEndpointConfig(MyEndpoint.class, "/wstest"));
        } catch (DeploymentException e) {
            e.printStackTrace();
        }
    }
}

And the Endpoint is:

@Component
public class MyEndpoint extends Endpoint {

    @Autowired
    MyService myService;

    @Override
    public void onOpen(Session session, EndpointConfig config) {

        session.addMessageHandler(new MyMessageHandler(session));
    }


    class MyMessageHandler implements MessageHandler.Whole<String> {

        final Session session;

        public MyMessageHandler(Session session) {
            this.session = session;
        }

        @Override
        public void onMessage(String message) {
            try {
                String greeting = myService.getGreeting();
                session.getBasicRemote().sendText(greeting + ", got your message (" + message + "). Thanks ! (session: " + session.getId() + ")");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Checkout the full source and ready to run example on my Github page.



回答2:

Try

@ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)

And add maven dependency

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


回答3:

You can make your @ServerEndpoint object extend SpringBeanAutowiringSupport. Then just make it aware of beans that gets constructed within a Spring-based web application this way:

  @PostConstruct
    public void init() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    }

This way @Autowired annotation will worl correctly:

@Autowired MyService myService;


回答4:

try this,it works for me

@Component
@ServerEndpoint(value = "/instantMessageServer",configurator = SpringConfigurator.class)
public class InstantMessageServer{
private static IChatService chatService;
    @Autowired
    public InstantMessageServer(IChatService chatService){
        this.chatService = chatService;
    }
    public InstantMessageServer(){}
}

I found this solution on https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support but there is one more glitch,the class annotated with @ServerEndpoint cant acquire httpsession with SpringConfigurator,there is no a override of method modifyhandler in it.Maybe we create a seperate Configurator extends SpringConfigurator and override that method would be a workaroud. It is better to build a real-time web application with spring-websocket and messaging api,I think.


public class ModifiedServerEndpointConfigurator extends SpringConfigurator{
    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
        super.modifyHandshake(sec, request, response);
    }
}


回答5:

You have to add bean definition in the configuration of spring.

The solution i found to integrate JSR 356 websocket @ServerEndpoint is to turn off the Servlet container's scan for WebSocket endpoints by spring which can be done by registering @Bean in your Spring Configuration. By this spring not overrides normal JSR 356 websocket by spring STOMP websocket which is the part of the websocket.

@ServerEndpoint(value="/chatMessage")
public class ChatEndpoint{
// Normal websocket body goes here.
}

Adding Beans in your Configuration as:

@Configuration
public class WebsocketConfig{
  @Bean
  public ChatEndpoint chatEndpoint(){
     return new ChatEndpoint();
  }
  // main one is ServerEndpointExporter which prevents Servlet container's scan for WebSocket
  @Bean
  public ServerEndpointExporter endpointExporter(){
     return new ServerEndpointExporter();
  }
}

This all done for you. But you should remove configurator = SpringConfigurator.class from @ServerEndpoint. I am using Spring Websocket 4.0.0 and it works fine. You can also see this Link.

If you alright then follow this Link also for concept.

Note that, Normally you should make websocket configuration separately from the main configuration of your spring.