extends ReactiveCrudRepository
}
Однако наиболее важно отметить, что помимо работы с Flux вместо Iterable,, а также того, как вы получаете этот Flux, программная модель для определения реактивного контроллера WebFlux ничем не отличается от нереактивного контроллера Spring MVC. Оба аннотируются с помощью @RestController и высокоуровневого @RequestMapping на уровне класса. И оба имеют функции обработки запросов, которые аннотируются с помощью @GetMapping на уровне метода. Разница только в том, какой тип возвращают методы обработчика.
Еще одно важное замечание заключается в том, что, хотя вы получаете Flux
ВОЗВРАЩЕНИЕ ОДИНОЧНЫХ ЗНАЧЕНИЙ
В качестве другого примера рассмотрим метод tacoById() из DesignTacoController, как он был написан в главе 6:
@GetMapping("/{id}")
public Taco tacoById(@PathVariable("id") Long id) {
Optional
if (optTaco.isPresent()) {
return optTaco.get();
}
return null;
}
Здесь этот метод обрабатывает запросы GET для /design/{id} и возвращает одиночный объект Taco. Поскольку findById() репозитория возвращает Optional, вам пришлось написать какой-то неуклюжий код, чтобы справиться с этим. Но предположим на минуту, что findById() возвращает Mono
@GetMapping("/{id}")
public Mono
return tacoRepo.findById(id);
}
Вау! Это намного проще. Однако более важно то, что, возвращая Mono
РАБОТА С ТИПАМИ RXJAVA
Стоит отметить, что хотя типы Reactor, такие как Flux и Mono, являются естественным выбором при работе с Spring WebFlux, вы также можете выбрать работу с типами RxJava, такими как Observable и Single. Например, предположим, что между DesignTacoController и внутренним репозиторием находится служба, которая работает в терминах типов RxJava. В этом случае метод recentTacos() может быть написан так:
@GetMapping("/recent")
public Observable
return tacoService.getRecentTacos();
}
Аналогично, метод tacoById() может быть написан для работы с RxJava Single, а не Mono:
@GetMapping("/{id}")
public Single
return tacoService.lookupTaco(id);
}
Кроме того, методы контроллера Spring WebFlux также могут возвращать RxJava Completable, который эквивалентен Mono
РЕАКТИВНАЯ ОБРАБОТКА ВХОДНЫХ ДАННЫХ
До сих пор мы интересовались только тем, какие реактивные типы возвращают методы контроллера. Но с Spring WebFlux вы также можете принять Mono или Flux в качестве входных данных для метода-обработчика. Для демонстрации рассмотрим оригинальную реализацию postTaco() из DesignTacoController:
@PostMapping(consumes="application/json")
@ResponseStatus(HttpStatus.CREATED)
public Taco postTaco(@RequestBody Taco taco) {
return tacoRepo.save(taco);
}
postTaco() не только возвращает простой объект Taco, но также принимает объект Taco, связанный с содержимым в теле запроса. Это означает, что postTaco() не может быть вызван до тех пор, пока полезная нагрузка запроса не будет полностью подготовлена для создания экземпляра объекта Taco. Это означает, что postTaco() не может быть возвращен, пока не завершится блокирующий вызов метода save() репозитория. Другими словами, запрос блокируется дважды: при поступлении в postTaco() и снова внутри postTaco(). Но, применив небольшое реактивное кодирование к postTaco(), вы можете сделать его полностью неблокирующим методом обработки запросов:
@PostMapping(consumes="application/json")
@ResponseStatus(HttpStatus.CREATED)
public Mono
return tacoRepo.saveAll(tacoMono).next();
}
Здесь postTaco() принимает Mono
Принимая Mono
Spring WebFlux является фантастической альтернативой Spring MVC, предлагая возможность написания реактивных веб-приложений с использованием той же модели разработки, что и Spring MVC. Но у Spring 5 есть еще одна новая хитрость. Давайте посмотрим, как создавать реагирующие API, используя новый функциональный стиль программирования Spring 5.
11.2 Определение функциональных обработчиков запросов
Модель программирования Spring MVC, основанная на аннотациях, существует начиная с Spring 2.5 и пользуется большой популярностью. Это имеет несколько недостатков.
Во-первых, любое программирование на основе аннотаций включает разделение в определении того, что аннотация должна делать и как она должна это делать. Сами аннотации определяют, что; а как это определено в другом месте в рамках кода. Это усложняет модель программирования, когда речь заходит о какой-либо настройке или расширении, поскольку такие изменения требуют работы в коде, внешнем по отношению к аннотации. Более того, отладка такого кода сложна, потому что вы не можете установить точку останова для аннотации.
Кроме того, поскольку популярность Spring продолжает расти, разработчики, впервые познакомившиеся с Spring в других языках и фреймворках, могут обнаружить что Spring MVC (и WebFlux) на основе аннотаций совсем не такой, как они уже его знают. В качестве альтернативы WebFlux Spring 5 представил новую модель функционального программирования для определения реактивных API.
Эта новая модель программирования используется больше как библиотека и меньше как фреймворк, позволяя сопоставлять запросы с кодом обработчика без аннотаций. Написание API с использованием модели функционального программирования Spring включает четыре основных типа:
-RequestPredicate — объявляет виды запросов, которые будут обработаны.
-RouterFunction - бъявляет, как соответствующий запрос должен быть направлен в код обработчика.
-ServerRequest - представляет собой HTTP-запрос, включая доступ к информации в header и body.
-ServerResponse - представляет ответ HTTP, включая информацию header и body
В качестве простого примера, который объединяет все эти типы, рассмотрим следующий пример Hello World:
package demo;
import static org.springframework.web.
reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.
reactive.function.server.RouterFunctions.route;
import static org.springframework.web.
reactive.function.server.ServerResponse.ok;
import static reactor.core.publisher.Mono.just;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
@Configuration
public class RouterFunctionConfig {
@Bean
public RouterFunction> helloRouterFunction() {
return route(GET("/hello"),
request -> ok().body(just("Hello World!"), String.class));
}
}
Первое, на что нужно обратить внимание, это то, что вы произвели статический импорт нескольких вспомогательных классов, которые вы можете использовать для создания вышеупомянутых функциональных типов. Вы также статически импортировали Mono, чтобы остальную часть кода было легче читать и понимать.
В этом классе аннотированном как @Configuration у вас есть один метод @Bean типа RouterFunction >. Как уже упоминалось, RouterFunction объявляет сопоставления между одним или несколькими объектами RequestPredicate и функциями, которые будут обрабатывать соответствующие запрос(ы).
Метод route() из RouterFunctions принимает два параметра: RequestPredicate и функцию для обработки совпадающих запросов. В этом случае метод GET() из RequestPredicates объявляет RequestPredicate, который совпадает с HTTP-запросами GET для пути /hello.
Что касается функции-обработчика, она написана как лямбда, хотя она также может быть ссылкой на метод. Хотя это явно не объявлено, лямбда-обработчик принимает ServerRequest в качестве параметра. Он возвращает ServerResponse, используя ok() из ServerResponse и body() из BodyBuilder, который был возвращен из ok(). Это было сделано для того, чтобы создать ответ с кодом состояния HTTP 200 (ОК) и полезной нагрузкой body с надписью Hello World!
Метод helloRouterFunction() объявляет RouterFunction, которая обрабатывает только один вид запроса. Но если вам нужно обработать запрос другого типа, вам не нужно писать другой метод @Bean, хотя вы можете это сделать. Вам нужно только вызвать andRoute(), чтобы объявить другое сопоставление RequestPredicate-to-function. Например, вот как вы можете добавить другой обработчик для запросов GET для /bye: