Сегодня мы напишем простое приложение с использованием Spring, технологии WebSocket и протокола STOMP, использующегося поверх WebSocket. Клиентская часть будет представлять собой HTML-страницу с клиентским JavaScript кодом.
Посмотреть проект можно на моем GitHub‘е: https://github.com/JavaGrinko/spring-websocket-example
Постановка задачи
Необходимо реализовать приложение, которое будет обновлять цвет фона клиентской страницы каждую секунду без перезагрузки страницы.
Реализация
Так, как необходимо менять цвет клиента без перезагрузки страницы будем использовать JavaScript и технологию WebSocket.
Для начала, создадим конфигурационный файл build.gradle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
group 'javagrinko' version '1.0' apply plugin: 'java' repositories { mavenCentral() } dependencies { compile("org.springframework.boot:spring-boot-starter-websocket:1.3.2.RELEASE") compile("org.springframework:spring-messaging:1.3.2.RELEASE") testCompile group: 'junit', name: 'junit', version: '4.11' } |
Данные зависимости обеспечат проект всеми библиотеками, необходимыми для поддержки WebSocket.
Создадим главный класс приложения с конфигурацией WebSocket:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package javagrinko.websocket; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; @SpringBootApplication @EnableScheduling @EnableWebSocketMessageBroker public class Main extends AbstractWebSocketMessageBrokerConfigurer { public static void main(String[] args) { SpringApplication.run(Main.class, args); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/color").withSockJS(); } } |
Аннотация @EnableWebSocketMessageBroker включает брокер соединений WebSocket.
Аннотация @EnableScheduling нам необходима для включения аннотаций планирования задач. Это понадобится для обеспечения отправки сообщений на изменение цвета фона каждую секунду.
В методе registerStompEndpoints() регистрируются все конечные точки, которые будут обслуживаться SockJsHttpRequestHandler. Если вы в браузере перейдете по пути http://localhost:8080/color то увидите сообщение “Welcome to SockJS!”.
Сделаем POJO-класс для сообщения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package javagrinko.websocket.dom; public class ColorMessage { private String colorString; public String getColorString() { return colorString; } public ColorMessage(String colorString) { this.colorString = colorString; } public ColorMessage() { } } |
Класс-контроллер, обрабатывающий входящие и исходящие сообщения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package javagrinko.websocket.controller; import javafx.scene.paint.Color; import javagrinko.websocket.dom.ColorMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Controller; import java.util.Random; @Controller public class ColorController { private Logger log = LoggerFactory.getLogger(ColorController.class); @Autowired private SimpMessagingTemplate simpMessagingTemplate; @MessageMapping("/color") public void receiveColor(ColorMessage message){ log.info("message.getColorString() = " + message.getColorString()); } @Scheduled(fixedDelay = 1000) private void bgColor(){ Random r = new Random(); Color rgb = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255), r.nextDouble()); String m = rgb.toString(); String color = m.replace("0x", "#"); simpMessagingTemplate.convertAndSend("/topic/color", new ColorMessage(color)); log.info("Send color: " + color); } } |
Метод receiveColor() слушает конечную точку /color и работает на прием сообщений.
Метод bgColor() аннотирован аннотацией @Scheduled и обеспечивает запуск этого метода каждую секунду. В методе формируется код произвольного цвета в формате HTML и отправляется сообщением через стандартный метод convertAndSend() класса SimpMessagingTemplate. Реализацией бина SimpMessagingTemplate нас обеспечивает проект spring-messaging.
Серверная часть закончена. Приступим к созданию клиента:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
<!DOCTYPE html> <html> <head> <title>Hello WebSocket</title> <script src="http://cdn.jsdelivr.net/sockjs/1.0.3/sockjs.min.js"></script> <script src="https://raw.githubusercontent.com/jmesnil/stomp-websocket/master/lib/stomp.js"></script> <script type="text/javascript"> var stompClient = null; function setConnected(connected) { document.getElementById('connect').disabled = connected; document.getElementById('disconnect').disabled = !connected; } function connect() { var socket = new SockJS('/color'); stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { setConnected(true); console.log('Connected: ' + frame); stompClient.subscribe('/topic/color', function(greeting){ setNewColor(JSON.parse(greeting.body).colorString); }); }); } function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function sendColor(color) { stompClient.send("/color", {}, JSON.stringify({ 'colorString': color })); } function setNewColor(colorString) { document.body.setAttribute("bgcolor", colorString); sendColor(colorString); } </script> </head> <body onload="disconnect()" bgcolor="#c0c0c0"> <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enable Javascript and reload this page!</h2></noscript> <div> <div> <button id="connect" onclick="connect();">Connect</button> <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button> </div> </div> </body> </html> |
Здесь используется две библиотеки: sockjs.min.js версии 1.0.3 и stomp.js. После успешного подключения происходит подписка на очередь /topic/color для получения сообщений. После принятия сообщения происходит его парсинг как JSON объекта и достается поле color. Далее, полученный цвет устанавливается как цвет фона и после этого отправляется обратно в конечную точку /color.
Таким образом, при работающем клиенте вы видим меняющийся цвет фона, а в логе IDE следующий текст:
1 2 3 4 5 |
2016-02-02 16:32:43.508 INFO 5040 --- [eBrokerSockJS-3] j.websocket.controller.ColorController : Send color: #9cccd9e5 2016-02-02 16:32:43.563 INFO 5040 --- [nboundChannel-7] j.websocket.controller.ColorController : message.getColorString() = #9cccd9e5 2016-02-02 16:32:44.511 INFO 5040 --- [eBrokerSockJS-3] j.websocket.controller.ColorController : Send color: #c0a73ab6 2016-02-02 16:32:44.518 INFO 5040 --- [nboundChannel-1] j.websocket.controller.ColorController : message.getColorString() = #c0a73ab6 и т.д. |
Вот и всё, наше приложение готово!
Для тестирования web-клиента я использовал браузер FireFox и плагин Firebug 2.0.13. Firebug позволяет отлаживать JavaScript код и мониторить лог.
Посмотреть или скачать проект можно на моем GitHub’е: https://github.com/JavaGrinko/spring-websocket-example
Мой профиль на GitHub: https://github.com/JavaGrinko
Пишите свои комментарии!
А как написать java клиент на нем, точнее андроид?
Есть различные клиентские библиотеки, например https://github.com/koush/AndroidAsync . Если не получится сделать – обращайтесь, попробуем вместе.
На него т.е