春季5反应堆中控制器与路由器的区别

Dee*_*mar 9 spring spring-boot reactive spring-webflux

现在有两种方法可以在Spring 5中公开http端点.

  1. 控制器:通过制作休息控​​制器.

    @RestController
    @RequestMapping("persons")
    public class PersonController { 
    
        @Autowired
        private PersonRepo repo;
    
        @GetMapping("/{id}")
        public Mono<Person> personById(@PathVariable String id){
            retrun repo.findById(id);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 路由器:通过路由器.例如:

    @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()));
    }
    
    Run Code Online (Sandbox Code Playgroud)

使用任何一种方法有任何性能差异吗?从头开始编写应用程序时,我应该使用哪一个.

pov*_*nko 43

编程范式:命令式 vs 函数式

在使用@Controller@RestController注解的情况下,我们同意基于注解的模型,在该模型中我们使用注解进行映射(不仅如此)并因此产生副作用(这在功能世界中是不允许的)来使我们的 API 工作。这种副作用可能是@Valid为请求的主体或@RequestMapping整个控制器的根路径提供内置 bean 验证的注释。

在另一方面,与路由器的功能,我们摆脱由任何副作用API实现的条件和直接委托它的功能链注释:router -> handler。这两个非常适合构建基本的反应块:一系列事件和两个主角,这些事件的发布者和订阅者。

MVC 传统:Servlets 堆栈与 Netty 堆栈

当我们在谈论@Controller 我要说的是,我们通常会想到在同步Java世界的条件:ServletsServletContextServletContainerInitializerDispatcherServlet 等等。即使我们将返回Mono从控制器,使我们的应用程序具有反应性的,我们仍然会在以下方面发挥Servlet 3.0规范,支持java.nio.*和运行在相同的 servlet 容器上,例如JettyTomcat。随后,我们将在这里使用相应的设计模式和方法来构建 Web 应用程序。

RouterFunction另一方面,受到源自异步 Java 世界的真正反应式方法的启发 - Netty及其Channel Model.

随后出现了一组用于响应式环境的新类及其 API:ServerRequestServerResponseWebFilter等。至于我,它们是Spring团队根据前几年维护框架和理解新的Web系统需求而设计的。这些要求的名称是Reactive Manifesto

用例

最近我的团队遇到了无法将SwaggerRouterFucntion端点集成的问题。它可以支持@Controlers,但 Spring 团队介绍了他们的解决方案 - Spring REST Docs,可以轻松连接到响应式WebTestClient。我在这里使用“连接”这个词是因为它遵循真正的反应性含义:代替 Swagger 的重载配置和副作用注释,您可以轻松地在测试中构建 API 文档,而根本不涉及您的工作代码。

2020 年更新:尽管现在 Spring Webflux 已经可以随后使用 OpenAPI 规范与 Swagger 集成,但它仍然缺乏配置的简单性和透明度,在我看来,这是作为过时 MVC 方法的一部分的结果。

关闭(意见)

由于没有性能影响,它可能会听到类似于“它绝对基于个人喜好使用什么”的内容。我同意这确实是个人偏好在两种选择中的选择:前进或后退,当你让自己在同一个领域呆上十年。我认为@ControllerSpring 团队完成了对响应式的支持,以使旧项目能够以某种方式与时间要求保持一致,并且至少有迁移的机会。如果您打算从头开始创建 Web 应用程序,请不要犹豫并使用引入的反应式堆栈。


Sur*_*yan 9

虽然有点晚了,但这可能对未来的读者有用。

通过切换到功能性路由声明:

  1. 您在一处维护所有路由配置
  2. 在访问传入的请求参数、路径变量和请求的其他重要组件方面,您可以获得与通常的基于注释的方法几乎相同的灵活性
  3. 您可以避免运行整个 Spring Framework 基础设施,这可能会减少应用程序的引导时间

关于第 3 点,在某些情况下,Spring 生态系统的整个功能(IoC、注解处理、自动配置)可能是多余的,因此减少了应用程序的整体启动时间。

在微型微服务、Amazon Lambda 和类似云服务的时代,重要的是提供允许开发人员创建具有几乎相同框架功能库的轻量级应用程序的功能。这就是为什么 Spring Framework 团队决定将此功能合并到 WebFlux 模块中的原因。

新的功能性 Web 框架允许您在不启动整个 Spring 基础架构的情况下构建 Web 应用程序。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() 
                           ) 
                ); 
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我是功能网络的新手。在去使用之前,我需要明确这些疑惑。1)我们不能在单个控制器中维护所有端点吗?2)没有@SpringBootApplication如何启用其他spring功能? (4认同)