Интеграция JSR-356 WebSocket @ServerEndpoint с компонентами Spring 3

Я использую Spring 3.2.5 без полной новой поддержки JSR-356 WebSockets.

Я хотел бы иметь ссылку на синглтон-бин в моем @ServerEndpoint Сервер WebSocket, который создается самим контейнером сервлета, а не в контексте Spring.

Какой чистый способ сделать это?

Мое текущее решение: я сделал @Service бин синглтона с экземпляром в статическом поле:

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

    public static WebSocketSupportBean getInstance() {
        return instance;
    }

    public WebSocketSupportBean() {
        instance = this;
    }

и просто получить его в @ServerEndpoint статическим методом, отключение пользователя, если возвращено значение null (если bean-компонент не создан при запуске сервера, но пользователь подключается):

5 ответов

Решение

Вы можете настроить веб-сокеты с помощью Spring Framework 3.x

Я разработал небольшое приложение для проверки концепции, чтобы продемонстрировать, как на основе SpringConfiguration Россена Стоянчева, выпущенного с spring-core 4.0.

Приложение устанавливает конечную точку сервера websocket с помощью URI /wstest который будет использовать @Autowired Spring Bean, чтобы выбрать приветственное слово и ответить на сообщение websocket.

Соединение websocket инициируется, и сообщения отправляются html-страницей (index.html) работает в браузере, который поддерживает веб-сокеты.

Регистрация конечной точки выполняется ServletContextListener при инициализации контекста, и когда создается конечная точка, она будет связана с пружиной:

@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();
        }
    }
}

И конечная точка:

@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();
            }
        }
    }
}

Ознакомьтесь с полным исходным кодом и готовым примером на моей странице Github.

Вы должны добавить определение бина в конфигурацию весны.

Решение, которое я нашел для интеграции веб-сокета JSR 356 @ServerEndpoint это отключить проверку контейнера Servlet для конечных точек WebSocket весной, что можно сделать, зарегистрировав @Bean в своей конфигурации Spring. Эта пружина не перекрывает обычную веб-розетку JSR 356 с помощью пружинной веб-розетки STOMP, которая является частью веб-розетки.

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

Добавление Beans в вашу конфигурацию как:

@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();
  }
}

Это все сделано для вас. Но вы должны удалить configurator = SpringConfigurator.class от @ServerEndpoint, Я использую Spring Websocket 4.0.0, и он работает нормально. Вы также можете увидеть эту ссылку.

Если у вас все в порядке, перейдите по этой ссылке также для концепции.

Обратите внимание, что, как правило, вы должны выполнять настройку websocket отдельно от основной конфигурации вашей пружины.

Пытаться

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

И добавить Maven зависимость

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

Попробуй это, у меня работает

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

Я нашел это решение на https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support но есть еще один глюк: класс, аннотированный @ServerEndpoint, не может получить httpsession с SpringConfigurator в нем нет переопределения метода modifyhandler. Возможно, мы создадим отдельный Configurator, расширяющий SpringConfigurator, и переопределим, что метод будет обходным. Я думаю, что лучше создать веб-приложение в реальном времени с использованием Spring-Websocket и API обмена сообщениями.


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);
    }
}

Вы можете заставить свой объект @ServerEndpoint расширять SpringBeanAutowiringSupport. Затем просто сообщите ему о bean-компонентах, которые создаются в веб-приложении на базе Spring следующим образом:

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

Таким образом, аннотация @Autowired будет работать правильно:

@Autowired MyService myService;
Другие вопросы по тегам