全部都是面向函数编程,还记得大明湖畔的函数吗

109 阅读2分钟

「这是我参与2022首次更文挑战的第31天,活动详情查看:2022首次更文挑战」

核心组件

我将通过依次介绍HandlerFunction,RouterFunction以及FilterFunction 等核心组件来介绍整个框架。这三个接口以及本文中其他类型都可以在org.springframework.web.reactive.function包中找到。

处理功能

新框架的起点是HandlerFunction,其实质是Function>,其中的Request 和 Response都是新定义的不可变接口,提供了基础的对JDK8优化的HTTP消息描述DSL。有一个便捷的构造Response实例的构造器,与ResponseEntity中的十分相似。注解方式中与HandlerFunction相对应的是@RequestMapping所注解的方法。

如下是“Hello World”的处理方法,它返回了状态为200,body为字符串的消息。

    HandlerFunction<String> helloWorld =
 ​
      request -> Response.ok().body(fromObject("Hello World"));

如上,构建于Reactor之上的处理方法是完全的响应式的(reactive),它们可以接受Flux、Mono或者其他相应流(Reactive Streams)的发布者作为返回类型的参数。

需要注意的是处理方法本身是没有副作用的,因为它将response作为返回值,而不是作为参数(对比Servlet.service(ServletRequest,ServletResponse),其实质是BiConsumer)。无副作用的方法有很多好处:更有利于测试、构建和优化。

路由功能

入站请求是由RouterFunction,(即Function>)路由到HandlerFunction中去的。当满足条件匹配时,路由方法会执行处理方法,否则会返回一个空结果。路由方法与@RequestMapping注解的作用相似。但是,还有一个显著的区别:用注解时路由会被限制到注解的value所能表达的范围,处理这些方法的覆盖是困难的;当用路由方法的时候,代码就在那里,可以轻松的覆盖或替换。

如下是一个路由方法的例子,包含了一个行内的处理方法。这里看起来有一点冗余,不必担心,因为后面我们将会将它变得精简。

    RouterFunction<String> helloWorldRoute = 
 ​
      request -> {
 ​
        if (request.path().equals("/hello-world")) {
 ​
          return Optional.of(r -> Response.ok().body(fromObject("Hello World")));
 ​
        } else {
 ​
          return Optional.empty();
 ​
        }
 ​
      };

一般不用写完整的路由方法,而是静态引入RouterFunctions.route(),这样就可以用请求判断式(RequestPredicate) (即 Predicate)和处理方法(HandlerFunction)创建路由方法了。如果判断式判断成功则返回处理方法,否则返回空结果。如下是用route方法方式重写上面的例子:

    RouterFunction<String> helloWorldRoute =
 ​
      RouterFunctions.route(request -> request.path().equals("/hello-world"),
 ​
        request -> Response.ok().body(fromObject("Hello World")));

静态引入RequestPredicates.*后就可以使用那些常用的判断式了,如匹配路径、HTTP方法、content-type等。这样上面的例子将会变得更精简:

    RouterFunction<String> helloWorldRoute =
 ​
      RouterFunctions.route(RequestPredicates.path("/hello-world"),
 ​
        request -> Response.ok().body(fromObject("Hello World")));