Интеграция 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;