1. Обзор
В этой статье мы создадим простое веб-приложение, реализующее обмен сообщениями с использованием новых возможностей WebSocket, представленных в Spring Framework 4.0.
WebSockets — это двунаправленное , полнодуплексное , постоянное соединение между веб-браузером и сервером. Как только соединение WebSocket установлено, соединение остается открытым до тех пор, пока клиент или сервер не решит закрыть это соединение.
Типичным вариантом использования может быть ситуация, когда в приложении несколько пользователей общаются друг с другом, например, в чате. В нашем примере мы создадим простой чат-клиент.
2. Зависимости Maven
Поскольку это проект на основе Maven, мы сначала добавляем необходимые зависимости в pom.xml
:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
Кроме того, поскольку мы будем использовать JSON
для построения тела наших сообщений, нам нужно добавить зависимости Джексона .
Это позволяет Spring преобразовывать наш объект Java в/из JSON
:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
Если вы хотите получить самую новую версию вышеуказанных библиотек, ищите их на Maven Central .
3. Включить WebSocket весной
Первое, что нужно сделать, это включить возможности WebSocket. Для этого нам нужно добавить конфигурацию в наше приложение и аннотировать этот класс с помощью @EnableWebSocketMessageBroker
.
Как следует из названия, он позволяет обрабатывать сообщения WebSocket при поддержке брокера сообщений:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat");
registry.addEndpoint("/chat").withSockJS();
}
}
Здесь мы видим, что метод configureMessageBroker
используется для настройки брокера сообщений . Во-первых, мы разрешаем брокеру сообщений в памяти передавать сообщения клиенту по пунктам назначения с префиксом «/topic».
Мы завершаем нашу простую настройку, указав
префикс «/app» для фильтрации пунктов назначения, нацеленных на аннотированные методы приложения (через @MessageMapping ).
Метод registerStompEndpoints
регистрирует конечную точку «/chat», включая поддержку Spring STOMP . Имейте в виду, что мы также добавляем здесь конечную точку, которая работает без SockJS для эластичности.
Эта конечная точка с префиксом «/app» является конечной точкой, на которую сопоставляется метод ChatController.send() .
Он также включает резервные параметры SockJS , чтобы можно было использовать альтернативные параметры обмена сообщениями, если WebSockets недоступны. Это полезно, так как WebSocket еще не поддерживается во всех браузерах и может быть запрещен ограничивающими сетевыми прокси.
Резервные варианты позволяют приложениям использовать API WebSocket, но при необходимости во время выполнения изящно переходят на альтернативы, отличные от WebSocket.
4. Создайте модель сообщения
Теперь, когда мы создали проект и настроили возможности WebSocket, нам нужно создать сообщение для отправки.
Конечная точка будет принимать сообщения, содержащие имя отправителя и текст сообщения STOMP, тело которого является объектом JSON .
Сообщение может выглядеть так:
{
"from": "John",
"text": "Hello!"
}
Чтобы смоделировать сообщение, содержащее текст, мы можем создать простой `объект Java со свойствами
from и
text :`
public class Message {
private String from;
private String text;
// getters and setters
}
По умолчанию Spring будет использовать библиотеку Jackson
для преобразования объекта нашей модели в JSON и обратно.
5. Создайте контроллер обработки сообщений
Как мы видели, подход Spring к работе с обменом сообщениями STOMP заключается в связывании метода контроллера с настроенной конечной точкой. Это стало возможным благодаря аннотации @MessageMapping
.
Связь между конечной точкой и контроллером дает нам возможность обрабатывать сообщение, если это необходимо:
@MessageMapping("/chat")
@SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
Для целей нашего примера мы создадим еще один объект модели с именем OutputMessage
для представления выходного сообщения, отправленного в настроенное место назначения . Мы заполняем наш объект отправителем и текстом сообщения, взятым из входящего сообщения, и обогащаем его отметкой времени.
После обработки нашего сообщения мы отправляем его в соответствующий пункт назначения, указанный в аннотации @SendTo
. Сообщение получат все подписчики адресата « /topic/messages ».
6. Создайте браузерный клиент
После настройки на стороне сервера мы воспользуемся библиотекой sockjs-client для создания простой HTML-страницы, взаимодействующей с нашей системой обмена сообщениями.
Прежде всего, нам нужно импортировать клиентские библиотеки sockjs
и stomp
Javascript. Затем мы можем создать функцию connect()
для открытия связи с нашей конечной точкой, функцию sendMessage()
для отправки нашего сообщения STOMP и функцию разъединения()
для закрытия связи:
<html>
<head>
<title>Chat WebSocket</title>
<script src="resources/js/sockjs-0.3.4.js"></script>
<script src="resources/js/stomp.js"></script>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility
= connected ? 'visible' : 'hidden';
document.getElementById('response').innerHTML = '';
}
function connect() {
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/messages', function(messageOutput) {
showMessageOutput(JSON.parse(messageOutput.body));
});
});
}
function disconnect() {
if(stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendMessage() {
var from = document.getElementById('from').value;
var text = document.getElementById('text').value;
stompClient.send("/app/chat", {},
JSON.stringify({'from':from, 'text':text}));
}
function showMessageOutput(messageOutput) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(messageOutput.from + ": "
+ messageOutput.text + " (" + messageOutput.time + ")"));
response.appendChild(p);
}
</script>
</head>
<body onload="disconnect()">
<div>
<div>
<input type="text" id="from" placeholder="Choose a nickname"/>
</div>
<br />
<div>
<button id="connect" onclick="connect();">Connect</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">
Disconnect
</button>
</div>
<br />
<div id="conversationDiv">
<input type="text" id="text" placeholder="Write a message..."/>
<button id="sendMessage" onclick="sendMessage();">Send</button>
<p id="response"></p>
</div>
</div>
</body>
</html>
7. Проверка примера
Чтобы проверить наш пример, мы можем открыть пару окон браузера и получить доступ к странице чата по адресу:
http://localhost:8080
Как только это будет сделано, мы можем присоединиться к чату, введя псевдоним и нажав кнопку подключения. Если мы создадим и отправим сообщение, мы увидим его во всех сеансах браузера, присоединившихся к чату.
Взгляните на скриншот, чтобы увидеть пример:
8. Заключение
В этом руководстве мы изучили поддержку WebSocket в Spring. Мы видели его конфигурацию на стороне сервера и создали простой аналог на стороне клиента с использованием библиотек sockjs
и stomp
Javascript.
Код примера можно найти в проекте GitHub .