1. введение
Этот простой учебник демонстрирует использование нескольких асинхронных и потоковых объектов в Spring MVC 5.x.x.
В частности, мы рассмотрим три ключевых класса:
- ResponseBodyEmitter
- SseEmitter
- Потоковый ответ
Кроме того, мы обсудим, как взаимодействовать с ними с помощью клиента JavaScript.
2. ResponseBodyEmitter
ResponseBodyEmitter обрабатывает асинхронные ответы.
Кроме того, он представляет собой родительский элемент для ряда подклассов, один из которых мы рассмотрим подробнее ниже.
2.1. Серверная сторона
Лучше использовать ResponseBodyEmitter вместе с его собственным выделенным асинхронным потоком и обернутым ResponseEntity (в который мы можем напрямую ввести эмиттер ):
@Controller public class ResponseBodyEmitterController { private ExecutorService executor = Executors.newCachedThreadPool(); @GetMapping("/rbe") public ResponseEntityhandleRbe() { ResponseBodyEmitter emitter = new ResponseBodyEmitter(); executor.execute(() -> { try { emitter.send( "/rbe" + " @ " + new Date(), MediaType.TEXT_PLAIN); emitter.complete(); } catch (Exception ex) { emitter.completeWithError(ex); } }); return new ResponseEntity(emitter, HttpStatus.OK); } }
Таким образом, в приведенном выше примере мы можем обойти необходимость использования Completablefuture , более сложных асинхронных обещаний или использования аннотации @Async .
Вместо этого мы просто объявляем нашу асинхронную сущность и заключаем ее в новый Поток , предоставленный ExecutorService.
2.2. Клиентская сторона
Для использования на стороне клиента мы можем использовать простой метод XHR и вызывать наши конечные точки API так же, как в обычной операции AJAX:
var xhr = function(url) { return new Promise(function(resolve, reject) { var xmhr = new XMLHttpRequest(); //... xmhr.open("GET", url, true); xmhr.send(); //... }); }; xhr('http://localhost:8080/javamvcasync/rbe') .then(function(success){ //... });
3. SseEmitter
SseEmitter на самом деле является подклассом ResponseBodyEmitter и обеспечивает дополнительную Отправленное сервером событие (SSE) поддержку из коробки.
3.1. Серверная сторона
Итак, давайте быстро рассмотрим пример контроллера, использующего эту мощную сущность:
@Controller public class SseEmitterController { private ExecutorService nonBlockingService = Executors .newCachedThreadPool(); @GetMapping("/sse") public SseEmitter handleSse() { SseEmitter emitter = new SseEmitter(); nonBlockingService.execute(() -> { try { emitter.send("/sse" + " @ " + new Date()); // we could send more events emitter.complete(); } catch (Exception ex) { emitter.completeWithError(ex); } }); return emitter; } }
Довольно стандартный тариф, но мы заметим несколько отличий между этим и нашим обычным контроллером ОТДЫХА:
- Во-первых, мы возвращаем SseEmitter
- Кроме того, мы заключаем основную информацию об ответе в ее собственный Поток
- Наконец, мы отправляем информацию об ответе с помощью emitter.send()
3.2. Клиентская сторона
На этот раз наш клиент работает немного по-другому, так как мы можем использовать постоянно подключенное Отправленное сервером событие Библиотека:
var sse = new EventSource('http://localhost:8080/javamvcasync/sse'); sse.onmessage = function (evt) { var el = document.getElementById('sse'); el.appendChild(document.createTextNode(evt.data)); el.appendChild(document.createElement('br')); };
4. Потоковый ответ
Наконец, мы можем использовать Streaming ResponseBody для записи непосредственно в OutputStream перед передачей этой записанной информации обратно клиенту с помощью ResponseEntity.
4.1. Серверная сторона
@Controller public class StreamingResponseBodyController { @GetMapping("/srb") public ResponseEntityhandleRbe() { StreamingResponseBody stream = out -> { String msg = "/srb" + " @ " + new Date(); out.write(msg.getBytes()); }; return new ResponseEntity(stream, HttpStatus.OK); } }
4.2. Клиентская сторона
Как и раньше, мы будем использовать обычный метод XHR для доступа к контроллеру выше:
var xhr = function(url) { return new Promise(function(resolve, reject) { var xmhr = new XMLHttpRequest(); //... xmhr.open("GET", url, true); xmhr.send(); //... }); }; xhr('http://localhost:8080/javamvcasync/srb') .then(function(success){ //... });
Далее давайте рассмотрим некоторые успешные примеры использования этих примеров.
5. Сведение Всего Этого Воедино
После того, как мы успешно скомпилировали наш сервер и запустили наш клиент выше (доступ к предоставленному index.jsp ), мы должны увидеть следующее в нашем браузере: