菜单

Spring 5 新特点:函数式Web框架,springweb

2019年8月15日 - Java

Spring 5 新特性:函数式Web框架,springweb

举例

大家先从示例应用程序的部分摘录开头。上面是暴光Person对象的响应音讯库。很周围于守旧的,非响应消息库,只然而它回到Flux<Person>而守旧的回来List<Person>,以及再次来到Mono<Person>的地点重返Person。Mono<Void>用作完结标记:提议何时保存被成功。关于Reactor类型的越来越多音信,请参阅此博客小说

public interface PersonRepository {
    Mono<Person> getPerson(int id);
    Flux<Person> allPeople();
    Mono<Void> savePerson(Mono<Person> person);
}

上面是大家什么揭穿带有新的函数式web框架的财富库:

RouterFunction<?> route = route(GET("/person/{id}"),
    request -> {
        Mono<Person> person = Mono.justOrEmpty(request.pathVariable("id"))
            .map(Integer::valueOf)
            .then(repository::getPerson);
        return Response.ok().body(fromPublisher(person, Person.class));
    })
    .and(route(GET("/person"),
        request -> {
            Flux<Person> people = repository.allPeople();
        return Response.ok().body(fromPublisher(people, Person.class));
    }))
    .and(route(POST("/person"),
    request -> {
        Mono<Person> person = request.body(toMono(Person.class));
    return Response.ok().build(repository.savePerson(person));
}));

上面大家要介绍怎样运营,例如在Reactor Netty中:

HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter =
    new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create("localhost", 8080);
server.startAndAwait(adapter);

最终要做的一件事是试一试:

$ curl 'http://localhost:8080/person/1'
{"name":"John Doe","age":42}

上面还应该有越来越多介绍,让我们开采得越来越深!

 

举例

大家先从示例应用程序的一对摘录起先。上面是暴光Person指标的响应消息库。很类似于古板的,非响应音信库,只然而它回到Flux<Person>而守旧的回来List<Person>,以及重返Mono<Person>的地点回到Person。Mono<Void>用作达成标志:建议曾几何时保存被成功。关于Reactor类型的越来越多消息,请参阅此博客作品。

public interface PersonRepository {
    Mono<Person> getPerson(int id);
    Flux<Person> allPeople();
    Mono<Void> savePerson(Mono<Person> person);
}

下边是大家怎么暴光带有新的函数式web框架的能源库:

RouterFunction<?> route = route(GET("/person/{id}"),
    request -> {
        Mono<Person> person = Mono.justOrEmpty(request.pathVariable("id"))
            .map(Integer::valueOf)
            .then(repository::getPerson);
        return Response.ok().body(fromPublisher(person, Person.class));
    })
    .and(route(GET("/person"),
        request -> {
            Flux<Person> people = repository.allPeople();
        return Response.ok().body(fromPublisher(people, Person.class));
    }))
    .and(route(POST("/person"),
    request -> {
        Mono<Person> person = request.body(toMono(Person.class));
    return Response.ok().build(repository.savePerson(person));
}));

下边大家要介绍怎么样运行,比方在Reactor Netty中:

HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter =
    new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create("localhost", 8080);
server.startAndAwait(adapter);

最后要做的一件事是试一试:

$ curl 'http://localhost:8080/person/1'
{"name":"John Doe","age":42}

上面还大概有越来越多介绍,让我们开采得越来越深!

 

主导组件

作者会通过彻底表达为主器件来介绍框架:HandlerFunction``,RouterFunction,以及FilterFunction。那多个接口以及文中描述的持有其余种类都得以在org.springframework.web.reactive.function包中找到。

HandlerFunction

这一新框架的源点是HandlerFunction<T>,基本上是Function<Request,
Response<T>>,当中Request和Response是新定义的,一成不改变的分界面友好地来提供JDK-8
DSL到底层HTTP信息。对于营造Response实体是八个便利的营造筑工程具,极度附近于在ResponseEntity中看到的。对应到HandlerFunction讲明是二个富含@RequestMapping的艺术。

下边是二个简便的“Hello
World”管理函数的例子,再次来到有200境况以及body为String的响应音信:

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

正如小编辈在上边包车型的士事例中看出的,管理函数是透过营造在Reactor的基础上而浑然响应:它们承受Flux,Mono,或任何别的相应的流Publisher作为响应类型。

要细心的少数,HandlerFunction本人是从未有过副功能的,因为它回到响应,并非把它看成一个参数(参见Servlet.service(ServletRequest,ServletResponse),那实质上是BiConsumer<ServletRequest,ServletResponse> )。未有副成效有点不清功利:易于测量试验,编写和优化。

 

RouterFunction

传播的呼吁被路由到有RouterFunction<T>的管理函数(即Function<Request,
Optional<HandlerFunction<T>>)路由各管理函数,尽管它格外的话;不然就重临三个空的结果。路由方法与@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<Request>)和拍卖方法(HandlerFunction)创立路由艺术了。假使决断式剖断成功则赶回管理措施,不然再次回到空结果。如下是用route方法格局重写上边的例证:

RouterFunction<String> helloWorldRoute =
    RouterFunctions.route(request -> request.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.and(),像这么组合多个路由函数:

RouterFunction<?> route =
    route(path("/hello-world"),
        request -> Response.ok().body(fromObject("Hello World")))
    .and(route(path("/the-answer"),
        request -> Response.ok().body(fromObject("42"))));

纵然路线相称/hello-world,以少校回应“Hello
World”,假设相配/the-answer,则相同的时间再次来到“42”。假设双方都不相称,则赶回二个空的Optional。请小心,组合的路由函数会挨个实践,由此在切实函数从前归入泛型函数是有含义的。

您也足以整合需要谓词,通过调用and或or。专门的学业措施是这么:对于and,假设多个给定谓词相称的话,结果谓词相配,而只要两个中的三个谓语相称的话,那么就or匹配。举例:

RouterFunction<?> route =
    route(method(HttpMethod.GET).and(path("/hello-world")), 
        request -> Response.ok().body(fromObject("Hello World")))
    .and(route(method(HttpMethod.GET).and(path("/the-answer")), 
        request -> Response.ok().body(fromObject("42"))));

事实上,在RequestPredicates意识的大多谓词是结合的!举例,RequestPredicates.GET(String)是RequestPredicates.method(HttpMethod)和RequestPredicates.path(String)的组合物。因而,大家可以将方面包车型客车代码重写为:

RouterFunction<?> route =
    route(GET("/hello-world"),
        request -> Response.ok().body(fromObject("Hello World")))
    .and(route(GET("/the-answer"),
        request -> Response.ok().body(fromObject(42))));

 

主意援用

顺手说一句:到前段时间甘休,我们早就编写制定了颇具的管理函数作为内联的lambda表明式。就算那在演示和短的例子中展现优良,不过只可以说那有一种会促成“混乱”的偏向,因为你要掺杂三种担心:诉求路由和伸手管理。由此,大家要拜谒是或不是能够让事情变得更简明。首先,大家成立三个暗含管理代码的类:

class DemoHandler {
    public Response<String> helloWorld(Request request) {
        return Response.ok().body(fromObject("Hello World"));
    }
    public Response<String> theAnswer(Request request) {
        return Response.ok().body(fromObject("42"));
    }
}

在意,八个办法都有一个一双两好了管理函数的申明。那允许大家运用办法援引:

DemoHandler handler = new DemoHandler(); // or obtain via DI
RouterFunction<?> route =
    route(GET("/hello-world"), handler::helloWorld)
    .and(route(GET("/the-answer"), handler::theAnswer));

 

FilterFunction

由路由函数映射的门径能够透过调用RouterFunction.filter(FilterFunction<T,
凯雷德>)举行过滤,个中FilterFunction<T,大切诺基>本质上是BiFunction<Request,
HandlerFunction<T>,
Response<Lacrosse>>。函数的微管理器(handler)参数代表的正是漫天链条中的下一项:
这是多个独占鳌头的 HandlerFunction,
但即使叠加了多个过滤器的话,它也能够是别的的七个FilterFunction。让我们向路由加多四个日记过滤器:

// http://www.manongjc.com
RouterFunction<?> route =
    route(GET("/hello-world"), handler::helloWorld)
    .and(route(GET("/the-answer"), handler::theAnswer))
    .filter((request, next) -> {
        System.out.println("Before handler invocation: " + request.path());
        Response<?> response = next.handle(request);
        Object body = response.body();
        System.out.println("After handler invocation: " + body);
    return response;
});

急需当心的是,要不要调用下二个管理程序是可选的。那在安全和缓存方案中格外管用(如只在用户有丰裕权限的时候调用next)。

由于route是多少个特别路由函数,因此我们知道接下去的管理程序会回去什么项指标响应消息。那正是干什么大家最终在大家的过滤器中用Response<?>甘休以及用Object响应body的来头。在管理程序类中,三种办法都回去Response<String>,所以理应有希望有String一呼百应中央。大家能够透过利用RouterFunction.and萨姆e()来代替and()做到那或多或少。这种组合形式需求参数路由函数是完全一样的等级次序。举例,大家得以让具有的响应形成大写:

RouterFunction<String> route =
  route(GET("/hello-world"), handler::helloWorld)
  .andSame(route(GET("/the-answer"), handler::theAnswer))
  .filter((request, next) -> {
    Response<String> response = next.handle(request);
    String newBody = response.body().toUpperCase();
    return Response.from(response).body(fromObject(newBody));
  });

应用评释,相似的效果可以用@ControllerAdvice和/或ServletFilter来达成。

 

主导组件

作者会通过深透表明为主组件来介绍框架:HandlerFunction``,RouterFunction,以及FilterFunction。那七个接口以及文中描述的装有其余种类都足以在org.springframework.web.reactive.function包中找到。

HandlerFunction

这一新框架的起点是HandlerFunction<T>,基本上是Function<Request,
Response<T>>,个中Request和Response是新定义的,照猫画虎的分界面友好地来提供JDK-8
DSL到底层HTTP音讯。对于构建Response实体是二个便利的营造筑工程具,特别邻近于在ResponseEntity中见到的。对应到HandlerFunction讲授是一个富含@RequestMapping的章程。

上面是一个简单的“Hello
World”管理函数的事例,重临有200意况以及body为String的响应新闻:

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

正如小编辈在地方的例证中来看的,管理函数是由此营造在Reactor的根基上而浑然响应:它们承受Flux,Mono,或其余别的相应的流Publisher用作响应类型。

要小心的一点,HandlerFunction自己是未曾副成效的,因为它回到响应,并不是把它看成三个参数(参见Servlet.service(ServletRequest,ServletResponse),那精神上是BiConsumer<ServletRequest,ServletResponse> )。未有副功效有过多好处:易于测验,编写和优化。

 

RouterFunction

传播的伸手被路由到有RouterFunction<T>的管理函数(即Function<Request,
Optional<HandlerFunction<T>>)路由到处理函数,倘诺它相当的话;不然就回去三个空的结果。路由方法与@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<Request>)和拍卖方法(HandlerFunction)创立路由艺术了。假使剖断式判别成功则赶回管理措施,不然重返空结果。如下是用route方法方式重写上边的事例:

RouterFunction<String> helloWorldRoute =
    RouterFunctions.route(request -> request.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.and(),像这样组合三个路由函数:

RouterFunction<?> route =
    route(path("/hello-world"),
        request -> Response.ok().body(fromObject("Hello World")))
    .and(route(path("/the-answer"),
        request -> Response.ok().body(fromObject("42"))));

借使路径相配/hello-world,以上将回应“Hello
World”,假如相配/the-answer,则同有时候重回“42”。若是双方都不相称,则赶回多少个空的Optional。请小心,组合的路由函数会挨个推行,由此在实际函数从前放入泛型函数是有意义的。

您也能够结合必要谓词,通过调用and或or。工作格局是这么:对于and,假诺八个给定谓词相配的话,结果谓词相配,而要是两方中的四个谓语相称的话,那么就or相配。例如:

RouterFunction<?> route =
    route(method(HttpMethod.GET).and(path("/hello-world")), 
        request -> Response.ok().body(fromObject("Hello World")))
    .and(route(method(HttpMethod.GET).and(path("/the-answer")), 
        request -> Response.ok().body(fromObject("42"))));

事实上,在RequestPredicates意识的相当多谓词是结合的!比方,RequestPredicates.GET(String)是RequestPredicates.method(HttpMethod)和RequestPredicates.path(String)的组合物。由此,我们能够将上面的代码重写为:

RouterFunction<?> route =
    route(GET("/hello-world"),
        request -> Response.ok().body(fromObject("Hello World")))
    .and(route(GET("/the-answer"),
        request -> Response.ok().body(fromObject(42))));

 

办法援引

附带说一句:到近来截至,大家早就编写制定了有着的处理函数作为内联的lambda表达式。纵然那在示范和短的例证中展现不错,不过只好说那有一种会招致“混乱”的偏侧,因为你要掺杂三种思念:必要路由和伸手管理。由此,大家要会见是否能够让职业变得更轻便。首先,大家成立八个包蕴管理代码的类:

class DemoHandler {
    public Response<String> helloWorld(Request request) {
        return Response.ok().body(fromObject("Hello World"));
    }
    public Response<String> theAnswer(Request request) {
        return Response.ok().body(fromObject("42"));
    }
}

瞩目,四个方法皆有八个格外了管理函数的评释。那允许大家应用办法引用:

DemoHandler handler = new DemoHandler(); // or obtain via DI
RouterFunction<?> route =
    route(GET("/hello-world"), handler::helloWorld)
    .and(route(GET("/the-answer"), handler::theAnswer));

 

FilterFunction

由路由函数映射的路径能够透过调用RouterFunction.filter(FilterFunction<T,
ENVISION>)实行过滤,其中FilterFunction<T,Wrangler>本质上是BiFunction<Request,
HandlerFunction<T>,
Response<CRUISER>>。函数的管理器(handler)参数代表的就是任何链条中的下一项:
那是三个优良的 HandlerFunction,
但假使叠加了八个过滤器的话,它也能够是另外的二个FilterFunction。让大家向路由加多一个日记过滤器:

// http://www.manongjc.com
RouterFunction<?> route =
    route(GET("/hello-world"), handler::helloWorld)
    .and(route(GET("/the-answer"), handler::theAnswer))
    .filter((request, next) -> {
        System.out.println("Before handler invocation: " + request.path());
        Response<?> response = next.handle(request);
        Object body = response.body();
        System.out.println("After handler invocation: " + body);
    return response;
});

亟待当心的是,要不要调用下一个管理程序是可选的。那在安全和缓存方案中格外管用(如只在用户有充分权限的时候调用next)。

由于route是二个特别路由函数,因而我们驾驭接下去的管理程序会回去什么项目的响应消息。那正是干吗我们最终在大家的过滤器中用Response<?>甘休以及用Object一呼百应body的来由。在管理程序类中,二种方法都回到Response<String>,所以应该有相当大希望有String响应大旨。大家得以由此接纳RouterFunction.and萨姆e()来代替and()做到那点。这种结合格局要求参数路由函数是同样的等级次序。举个例子,大家能够让具有的响应产生大写:

RouterFunction<String> route =
  route(GET("/hello-world"), handler::helloWorld)
  .andSame(route(GET("/the-answer"), handler::theAnswer))
  .filter((request, next) -> {
    Response<String> response = next.handle(request);
    String newBody = response.body().toUpperCase();
    return Response.from(response).body(fromObject(newBody));
  });

动用证明,相似的法力能够用@ControllerAdvice和/或ServletFilter来完结。

 

运营服务端

负有这一体都很好,但有一件事忘了:大家如何能力在实际上的HTTP服务器中运作那一个函数呢?答案勿庸置疑是由此调用另叁个函数。你能够通过应用RouterFunctions.toHttpHandler()将路由函数转变来HttpHandler。HttpHandler是推荐到Spring
5.0 M1的三个响应抽象:它同意你运维在各类响应运营时上:Reactor
Netty、LX570xNetty、Servlet
3.1+,和Undertow。在那么些事例中,大家曾经注解了在Reactor
Netty中运作route是哪些的。对于汤姆cat,它看起来像这么:

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实用工具类,使用应用程序上下文是可选的:你能够在上下文中接通管理程序和路由函数,但它不是少不了的。

还要小心的是,你也可以转变路由函数为HandlerMapping,以便它能够在DispatcherHandler中运作(只怕须要有响应的@Controllers)。

 

运作服务端

享有那全部都很好,但有一件事忘了:大家怎么着才干在骨子里的HTTP服务器中运作这么些函数呢?答案勿庸置疑是经过调用另多个函数。你可以透过动用RouterFunctions.toHttpHandler()将路由函数调换到HttpHandler。HttpHandler是引入到Spring
5.0 M1的一个响应抽象:它同意你运转在各个响应运维时上:Reactor
Netty、瑞虎xNetty、Servlet
3.1+,和Undertow。在那一个事例中,大家早已评释了在Reactor
Netty中运作route是如何的。对于汤姆cat,它看起来像这么:

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实用工具类,使用应用程序上下文是可选的:你能够在前后文中接通管理程序和路由函数,但它不是必不可缺的。

还要小心的是,你也得以调换路由函数为HandlerMapping,以便它能够在DispatcherHandler中运转(只怕须要有响应的@Controllers)。

 

结论

让自家经过简单的下结论来得出结论:

为了让大家能更周详的刺探,我已经创设了多少个用到泛函web框架的简约示例项目。你可以在GitHub上找到那么些体系。款待留下您的理念!

原作地址:http://www.manongjc.com/article/1590.html

结论

让自家经过简单的计算来得出结论:

为了让大家能更宏观的垂询,笔者已经创办了二个行使泛函web框架的简要示例项目。你能够在GitHub上找到那些项目。款待留下你的视角!

原稿地址:http://www.manongjc.com/article/1590.html

http://www.bkjia.com/Javabc/1170781.htmlwww.bkjia.comtruehttp://www.bkjia.com/Javabc/1170781.htmlTechArticleSpring 5 新特点:函数式Web框架,springweb 比如大家先从示例应用程序的一对摘录初始。下边是揭露 Person
对象的响应新闻库。很临近于守旧的…

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图