Java Spring 5 Новая функциональная функциональная веб -структура
Привести пример
Давайте начнем с некоторых выдержек из приложения образца. Ниже приведена информационная библиотека ответа, которая разоблачает объекты человека. Очень похоже на традиционную, не реагирующую информационную библиотеку, за исключением того, что она возвращает Flux <dersne> и традиционный список возврата <Person> и место, где Mono <Person> возвращает человека. Mono <void> используется в качестве флага завершения: указывает, когда сохранение завершено.
Public Interface PersonRepository {mono <Person> getPerson (INT ID); Flux <Person> allpeople (); Mono <void> saveperson (mono <person> person);}Вот как мы выставляем библиотеку с новой функциональной веб -структурой:
RouterFunction <?> Route = route (get ("/person/{id}"), запрос -> {mono <person> person = mono.justorempty (request.pathvariable ("id")) .map (integer :: valueof) .Then (repository :: getperson); return outs.ok (). . и (route (get ("/person"), request -> {flux <person> people = repository.allpeople (); return response.ok (). Body (from Publisher (people, person.class));})). И (маршрут (""/person "), запрос -> {mono <person> exect.body.body (tomono (person.class); Response.ok (). Build (repository.saveperson (человек));}));Здесь мы представим, как запустить, например, в реакторе Netty:
Httphandler httphandler = routerfunctions.tohttphandler (маршрут); reactorhttphandlerAdapter adapter = new ReactorhttphandlerAdapter (httphandler); httpserver server = httpserver.create ("localhost", 8080);Последнее, что нужно сделать, это попробовать:
$ curl 'http: // localhost: 8080/person/1' {"name": "Джон Доу", "Возраст": 42}Ниже больше введений, давайте копаем глубже!
Основные компоненты
Я представлю структуру, тщательно объяснив основные компоненты: обработчика, маршрутизатор и фильтровая функция. Эти три интерфейса, а также все другие типы, описанные в статье, можно найти в Org.springframework.web.reactive.function.
Обработчика
Отправной точкой этой новой структуры является HandlerFunction <T>, которая в основном является функцией <запрос, ответ <T >>, где запрос и ответ вновь определены, а неизвестный интерфейс удобен для предоставления JDK-8 DSL для базовых HTTP-сообщений. Это удобный инструмент для строительства объектов, очень похожий на то, что вы видите в ответе. Соответствует аннотации обработки функции - это метод с @requestmapping.
Вот простой пример функции обработки «Hello World», возвращая ответное сообщение с 200 состояниями и массой строки:
HandlerFunction <String> helloWorld = request -> response.ok (). Body (fromObject ("Hello World"));Как мы видели в приведенном выше примере, функции обработки полностью отзывчивы, создавая реакторную основу: они принимают Flux, Mono или любого другого соответствующего издателя потока в качестве типа ответа.
Отнесее отметить, что сама обработчика не имеет побочных эффектов, потому что она возвращает ответ вместо того, чтобы рассматривать его как параметр (см. Servlet.service (ServletRequest, Servletresponse), который, по сути, является биконсором <servletRequest, servletresponse>). Существует много преимуществ без побочных эффектов: легко тестировать, записать и оптимизировать.
Роутерфункция
Входящий запрос направляется на функцию обработчика с помощью маршрутизации <T> (то есть функция <запрос, необязательный <HandlerFunction <T >>) и направляется в обработчик, если он соответствует; В противном случае возвращается пустой результат. Метод маршрутизации работает аналогично аннотации @requestmapping. Тем не менее, есть еще одна значительная разница: при использовании аннотаций маршрут будет ограничен диапазоном, который может выразить аннотированное значение, и трудно справиться с наложением этих методов; При использовании метода маршрутизации код существует и может быть легко перезаписан или заменен.
Ниже приведен пример функции маршрутизации со встроенной функцией обработки. Это выглядит немного длинным, но не волнуйтесь: мы найдем способ сделать его короче.
Routerfunction <string> helloworldroute = request -> {if (request.path (). Equals ("/hello -world"))) {return optacto.of (r -> response.ok (). Body (forebject ("hello world"))); } else {return optainAl.empty (); }};Как правило, нет необходимости писать полный метод маршрутизации, но вместо этого статически вводить routerfunctions.route (), чтобы вы могли создать метод маршрутизации, используя формулу решения запроса (запрос predicate <quest>) и Handlerfunction). Если решение будет успешным, метод обработки будет возвращен, в противном случае пустой результат будет возвращен. Ниже приведен пример, используя метод маршрута:
RouterFunction <String> helloworLdroute = routerFunctions.Route (request -> requent.path (). Equals ("/hello -world"), request -> response.ok (). Body (fromobject ("hello world"));Вы можете (статически) импортировать requestpredicates.* Чтобы получить доступ к обычно используемым предикатам, на основе пути, методов HTTP, типов контента и т. Д. С ним мы можем сделать Helloworldroute проще:
RouterFunction <String> helloworLdroute = routerFunctions.Route (requestPredicates.path ("/hello -world"), request -> response.ok (). Body (fromobject ("hello world"));Комбинированные функции
Две функции маршрутизации могут сформировать новую функцию маршрутизации, маршрут для любой функции обработки: если первая функция не соответствует, то вторая выполняется. Вы можете объединить две функции маршрутизации, подобные этой, вызывая маршрутизацию. И ():
Routerfunction <?> Route = route (path ("/hello -world"), request -> response.ok (). Body (fromobject ("hello world"))). И (route (path ("/the -answer"), запрос -> response.ok (). Body (fromobject ("42"))));Если путь соответствует /Hello-World, вышеупомянутое ответит на «Hello World», и если /если /the-answer соответствует, он вернется «42» одновременно. Если ни один из них не соответствует, возвращается пустой необязательный. Обратите внимание, что комбинированные функции маршрутизации выполняются в последовательности, поэтому имеет смысл поставить общие функции перед конкретной функцией.
Вы также можете объединить предикаты запроса, вызывая или или или. Это работает так: для и, если два данных предиката соответствуют, результат предикаты соответствуют, и если одно из двух совпадений, то или совпадает. Например:
RouterFunction <?> Route = route (method (httpmethod.get). И (path ("/hello -world")), request -> response.ok (). Body (fromobject ("hello world"))). И (route (method (httpmethod.get) Response.ok (). Body (fromObject ("42"))));На самом деле, большинство предикатов, найденных в запросах, объединены! Например, requestpredicates.get (string) - это состав requestpredicates.method (httpmethod) и requestpredicates.path (string). Поэтому мы можем переписать приведенный выше код как:
Routerfunction <?> Route = route (get ("/hello -world"), request -> response.ok (). Body (fromobject ("hello world")). И (route (get ("/the -answer"), запрос -> response.ok (). Body (forebject (42))));Ссылка на метод
Кстати: до сих пор мы написали все функции обработки как встроенные выражения Lambda. Хотя это хорошо работает в демонстрационных и коротких примерах, нужно сказать, что существует тенденция вызывать «путаницу», потому что вы хотите смешать две проблемы: запрос маршрутизации и обработки запросов. Итак, мы хотим посмотреть, может ли это сделать все проще. Во -первых, мы создаем класс, который содержит код обработки:
Класс Demohandler {public response <string> helloworld (запрос запроса) {return response.ok (). body (fromObject ("hello world")); }/ * http://www.manongjc.com/article/1590.html */public response <string> theAnswer (запрос запроса) {return response.ok (). body (fromObject ("42")); }}Обратите внимание, что оба метода имеют флаг, который совместим с функцией обработки. Это позволяет нам использовать ссылки на метод:
Demohandler Handler = новый Demohandler (); // или получить через dirouterfunction <?> route = route (get ("/hello-world"), handler :: helloworld). и (route (get ("/the-answer"), обработчик :: theAnswer));Фильтровая функция
Путь, отображаемый функцией маршрутизации, может быть отфильтрован путем вызова RouterFunction.Filter (FilterFunction <T, R>), где FilterFunction <T, R> по существу бифункция <запрос, HandlerFunction <T>, ответ <R >>. Параметр обработчика функции представляет следующий элемент во всей цепочке: это типичная обработчика, но если подключено несколько фильтров, это также может быть еще одной фильтровальной функцией. Давайте добавим фильтр журнала в маршрут:
// http: //www.manongjc.comrouterfunction <?> route = route (get ("/hello-world"), handler :: helloworld). и (route (get ("/the-answer"), handler :: theanswer)) .filter ((запрос, следующий)-> {system.out.print.println ("до handler hippose:"). Response <?> Response = next.handle (запрос);Следует отметить, что вызовать следующего обработчика является необязательным. Это очень полезно в схемах безопасности и кэширования (например, призыв к следующему только тогда, когда у пользователя есть достаточные разрешения).
Поскольку маршрут является бесконечной функцией маршрутизации, мы знаем, какой тип информации о ответе вернется. Вот почему мы получили ответ <?> В нашем фильтре и отвечаем на тело объектом. В классе обработчика оба метода возвращают ответ <string>, поэтому должно быть возможно иметь корпус ответа строки. Мы можем сделать это с помощью RouterFunction.andSame () вместо и (). Этот метод комбинации требует, чтобы функция маршрутизации параметров была одинаковой типа. Например, мы можем сделать все ответы заработать:
RouterFunction <String> route = route (get ("/hello-world"), handler :: helloworld) .andsame (route (get ("/the-answer"), handler :: theAnswer)) .filter ((запрос, следующий)-> {response <String> response = hade.hhah Response.from (response) .body (fromobject (newbody));Используя аннотации, аналогичные функции могут быть реализованы с использованием @ControllerAdvice и/или ServletFilter.
Запустите сервер
Все это нормально, но я забыл: как мы можем запустить эти функции на реальном HTTP -сервере? Ответ, несомненно, вызывает другую функцию. Вы можете преобразовать функцию маршрутизации в httphandler с помощью RouterFunctions.tohttphandler (). Httphandler - это абстракция ответа, введенная в Spring 5.0 M1: она позволяет работать на различных времени ответов: реактор Netty, Rxnetty, Servlet 3.1+ и Infertow. В этом примере мы показали, как это похоже на маршрут работы в реакторе Netty. Для Tomcat это выглядит так:
Httphandler httphandler = routerfunctions.tohttphandler (route); httpservlet servlet = new servlethttphandleradapter (httphandler); tomcat server = new tomcat (); context rootcontext = server.addcontext (",", System.getProperty ("java.io.tmpdir")); tomcat.addservlet (rootcontext, "Servlet", Servlet); RootContext.Addservletmapping ("/", "Servlet"); Tomcatserver.start ();Следует отметить, что приведенный выше код не зависит от контекста приложения Spring. Как и JDBctemplate и другие классы утилиты Spring, использование контекста приложения является необязательным: вы можете подключить функции обработчика и маршрутизации в контексте, но это не требуется.
Также обратите внимание, что вы также можете преобразовать функцию маршрутизации в ручную передачу, чтобы она могла работать в диспетчер -рудке (может потребовать отзывчивых @controllers).
в заключение
Позвольте мне сделать вывод через краткое изложение:
Чтобы дать вам более полное понимание, я создал простой пример проекта, используя функциональную веб -структуру. Скачать адрес
Спасибо за чтение, я надеюсь, что это поможет вам. Спасибо за поддержку этого сайта!