Разница между @Controller и RouterFunction в Spring 5 WebFlux

В Spring 5 есть два способа предоставить доступ к конечным точкам HTTP.

  1. @Controller или @RestController, создав класс контроллера, например
@RestController
@RequestMapping("persons")
public class PersonController { 

    @Autowired
    private PersonRepo repo;

    @GetMapping("/{id}")
    public Mono<Person> personById(@PathVariable String id){
        retrun repo.findById(id);
    }
}
  1. Маршрутизация в классе @Configuration с помощью функций RouterFunctions:
@Bean
public RouterFunction<ServerResponse> personRoute(PersonRepo repo) {
    return route(GET("/persons/{id}"), req -> Mono.justOrEmpty(req.pathVariable("id"))                                             
                                                 .flatMap(repo::getById)
                                                 .flatMap(p -> ok().syncBody(p))
                                                 .switchIfEmpty(notFound().build()));
}

Есть ли разница в производительности при использовании любого подхода? Какой из них использовать при написании приложения с нуля.


person Deepak Kumar    schedule 03.11.2017    source источник
comment
Это вопрос предпочтений, а не производительности.   -  person JB Nizet    schedule 04.12.2017
comment
Я до сих пор не понимаю, почему кому-то нужны роутеры. Это очень нечитабельно по сравнению с контроллерами. Может быть, я не понимаю ... РЕДАКТИРОВАТЬ: см. sparkbit.pl / spring-web-reactive-rest-controllers Преимущество этого подхода [функциональная веб-платформа] заключается в простоте и сокращении шаблонного кода, когда все, что вы хотите создать, - это очень небольшая услуга.   -  person jaw    schedule 06.03.2018
comment
На данный момент (весенняя загрузка 2.1) я предлагаю использовать Controller, а не по соображениям производительности, просто потому, что функции маршрутизатора не имеют таких функций, как проверка, интеграция swagger и т.д.   -  person nekperu15739    schedule 28.03.2019


Ответы (2)


Парадигма программирования: императив против функционального

В случае с аннотациями @Controller или @RestController мы согласны с моделью на основе аннотаций, в которой мы используем аннотации для сопоставлений (и не только) и, как следствие, побочные эффекты (что недопустимо в функциональном мире), чтобы наш API работал. . Такими побочными эффектами могут быть @Valid аннотация, обеспечивающая встроенную проверку bean-компонентов для тел запросов, или @RequestMapping корневой путь для всего контроллера.

С другой стороны, с помощью функций маршрутизатора мы избавляемся от аннотаций, которые содержат какие-либо побочные эффекты с точки зрения реализации API, и делегируем их непосредственно функциональной цепочке: router -> handler. Эти два идеально подходят для построения базового реактивного блока: последовательность событий и два главных героя, издатель и подписчик на эти события.

Наследие MVC: стек сервлетов против стека Netty

Когда мы говорим о @Controller, я бы сказал, что мы обычно думаем в терминах синхронного мира Java: Servlets, ServletContext, ServletContainerInitializer, DispatcherServlet и т. Д. Даже если мы вернем Mono из контроллера, чтобы сделать наше приложение реактивным, мы все равно будем играть в терминах спецификации Servlet 3.0, которая поддерживает java.nio.* и работает в тех же контейнерах сервлетов, как Jetty или Tomcat. Впоследствии здесь мы будем использовать соответствующие шаблоны проектирования и подходы для создания веб-приложений.

RouterFunction, с другой стороны, был вдохновлен истинным реактивным подходом, который происходит из мира асинхронной Java - Netty и ее Channel Model.

Впоследствии появился новый набор классов и их API для реактивной среды: ServerRequest, ServerResponse, WebFilter и другие. Что касается меня, они были разработаны командой Spring в соответствии с предыдущими годами поддержки фреймворка и понимания требований новых веб-систем. Эти требования называются Reactive Manifesto.

Сценарий использования

Недавно моя команда столкнулась с проблемой невозможности интеграции Swagger с RouterFucntion конечными точками. Он мог проголосовать за @Controlers, но команда Spring представила свое решение - Spring REST Docs, которое могло быть легко подключается к реактивному WebTestClient. И я использую здесь слово «подключен», потому что оно следует истинному реактивному значению: вместо Swagger с его перегруженными конфигурациями и аннотациями побочных эффектов вы легко можете создавать свои документы API в тестах, вообще не касаясь своего рабочего кода.

Обновление 2020: несмотря на то, что теперь Spring Webflux уже может быть интегрирован с Swagger с последующим использованием спецификации OpenAPI, ему по-прежнему не хватает простоты конфигурации и прозрачности, что, по моему скромному мнению, является следствием быть частью архаичного подхода MVC.

Закрытие (мнение)

Из-за отсутствия влияния на производительность, скорее всего, вы услышите что-то похожее на «это абсолютно зависит от индивидуальных предпочтений, что использовать». И я согласен с тем, что это действительно индивидуальное предпочтение среди двух вариантов: движение вперед или движение назад, когда вы позволяете себе оставаться в одной и той же сфере в течение десяти лет. Я думаю, что реактивная поддержка @Controller была сделана командой Spring, чтобы старые проекты как-то соответствовали требованиям времени и имели хотя бы возможность для миграции. Если вы собираетесь создать веб-приложение с нуля, не сомневайтесь и используйте представленный реактивный стек.

person Serhii Povísenko    schedule 27.07.2019

Хоть и поздно, но это может пригодиться будущим читателям.

Переключившись на объявление функционального маршрута:

  1. вы храните всю конфигурацию маршрутизации в одном месте
  2. вы получаете почти такую ​​же гибкость, как и обычный подход на основе аннотаций, с точки зрения доступа к параметрам входящего запроса, переменным пути и другим важным компонентам запроса.
  3. вы получаете возможность избежать запуска всей инфраструктуры Spring Framework, что может уменьшить время начальной загрузки приложения

Что касается пункта 3, то в некоторых случаях вся функциональность (IoC, обработка аннотаций, автоконфигурация) экосистемы Spring может быть избыточной, что снижает общее время запуска приложения.

В эпоху крошечных микросервисов, Amazon Lambdas и подобных облачных сервисов важно предлагать функциональные возможности, позволяющие разработчикам создавать легкие приложения с почти таким же арсеналом функций фреймворка. Вот почему команда Spring Framework решила включить эту функцию в модуль WebFlux.

Новый функциональный веб-фреймворк позволяет создавать веб-приложения, не запуская всю инфраструктуру Spring. Метод main в этом случае должен выглядеть примерно так (обратите внимание, что нет аннотации @SpringBootApplication)

class StandaloneApplication { 
    public static void main(String[] args) { 
        HttpHandler httpHandler = RouterFunctions.toHttpHandler(
           routes(new BCryptPasswordEncoder(18))
        ); 

        ReactorHttpHandlerAdapter reactorHttpHandler = new ReactorHttpHandlerAdapter(httpHandler); 

        HttpServer.create() 
            .port(8080) 
            .handle(reactorHttpHandler) 
            .bind() 
            .flatMap(DisposableChannel::onDispose) 
            .block(); 
    }

    static RouterFunction<ServerResponse> routes(PasswordEncoder passwordEncoder ) { 
        return
            route(
                POST("/check"), 
                request -> request 
                          .bodyToMono(PasswordDTO.class)
                          .map(p -> passwordEncoder 
                              .matches(p.getRaw(), p.getSecured())) 
                          .flatMap(isMatched -> isMatched 
                              ? ServerResponse 
                                  .ok() 
                                  .build() 
                              : ServerResponse 
                                  .status(HttpStatus.EXPECTATION_FAILED) 
                                  .build() 
                           ) 
                ); 
    }
}
person Suren Aznauryan    schedule 25.05.2020