Wie gestern in Jürgens Blog erwähnt, ist der zweite Meilenstein von Spring 5.0 die Einführung eines neuen funktionalen Web-Frameworks. In diesem Artikel werden wir weitere Informationen zu diesem Framework geben.
Beginnen wir mit einigen Auszügen aus einer Beispielanwendung. Unten finden Sie das Antwort-Repository, das das Person
-Objekt verfügbar macht. Sehr ähnlich zur herkömmlichen, nicht reagierenden Informationsbasis, außer dass sie FluxMono<Person>
ist zurückgekehrte Person. Mono
public interface PersonRepository { Mono<Person> getPerson(int id); Flux<Person> allPeople(); Mono<Void> savePerson(Mono<Person> person); }
So stellen wir das Repository mit dem neuen funktionalen Web-Framework bereit:
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)); }));
So führen wir es aus, zum Beispiel in Reactor Netty:
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route); ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler); HttpServer server = HttpServer.create("localhost", 8080); server.startAndAwait(adapter);
Das Letzte, was Sie tun müssen, ist, es auszuprobieren:
$ curl 'http://localhost:8080/person/1' {"name":"John Doe","age":42}
Weiter unten finden Sie mehr, also lasst uns tiefer graben!
Ich werde das Framework vorstellen, indem ich die Kernkomponenten ausführlich erkläre: HandlerFunction
,RouterFunction
und FilterFunction
. Diese drei Schnittstellen sowie alle anderen im Artikel beschriebenen Typen sind im Paket org.springframework.web.reactive.function zu finden.
HandlerFunction
Der Ausgangspunkt dieses neuen Frameworks ist HandlerFunction<T>
, bei dem es sich im Wesentlichen um FunctionRequest
und Antwort Es handelt sich um eine neu definierte, benutzerfreundliche Schnittstelle, die JDK-8 DSL für die zugrunde liegenden HTTP-Nachrichten bereitstellt. Zum Erstellen von Response
-Entitäten gibt es ein praktisches Build-Tool, das dem, was Sie in ResponseEntity sehen, sehr ähnlich ist. Der HandlerFunction
-Annotation entspricht eine Methode mit @RequestMapping.
Das Folgende ist ein Beispiel für eine einfache Verarbeitungsfunktion „Hello World“, die eine Antwortnachricht mit dem Status 200 und einem Textkörper von String zurückgibt:
HandlerFunction<String> helloWorld = request -> Response.ok().body(fromObject("Hello World"));
Wie wir im obigen Beispiel gesehen haben, sind Handlerfunktionen vollständig reaktiv, indem sie auf Reactor aufbauen: Sie akzeptieren Flux, Mono oder jeden anderen entsprechenden Stream Publisher
als Antworttyp.
Zu beachten ist, dass HandlerFunction
selbst keine Nebenwirkungen hat, da es die Antwort zurückgibt, anstatt sie als Parameter zu behandeln (siehe Servlet.service(ServletRequest,ServletResponse), was im Wesentlichen BiConsumer< ServletRequest ist, ServletResponse>). Keine Nebenwirkungen haben viele Vorteile: einfach zu testen, zu schreiben und zu optimieren.
RouterFunction
Eingehende Anfragen werden geroutet an die Handler-Funktion mit Das Folgende ist ein Beispiel einer Routing-Funktion mit integrierten Handlerfunktionen. Es mag etwas langatmig erscheinen, aber keine Sorge: Wir werden einen Weg finden, es kürzer zu machen. Im Allgemeinen ist es nicht notwendig, eine vollständige Routing-Methode zu schreiben, sondern statisch RouterFunctions.route() einzuführen, damit das Anforderungsprädikat (RequestPredicate) verwendet werden kann (d. h. Predicate< ;Request>) Erstellen Sie eine Routing-Methode mit der Verarbeitungsmethode (HandlerFunction). Wenn die Beurteilung erfolgreich ist, wird die Verarbeitungsmethode zurückgegeben, andernfalls wird ein leeres Ergebnis zurückgegeben. Hier ist das obige Beispiel, das mit der Route-Methode neu geschrieben wurde: Sie können RequestPredicates.* (statisch) importieren, um auf häufig verwendete Prädikate zuzugreifen und einen Abgleich basierend auf Pfad, HTTP-Methode, Inhaltstyp usw. durchzuführen. Damit können wir helloWorldRoute einfacher machen: Kombinationsfunktion Zwei Routing-Funktionen können eine neue Routing-Funktion bilden und zu einer der beiden Routing-Funktionen weiterleiten: Wenn die Wenn die erste Funktion nicht übereinstimmt, wird die zweite ausgeführt. Sie können zwei Routenfunktionen wie diese kombinieren, indem Sie RouterFunction.and() aufrufen: Wenn der Pfad mit /hello-world übereinstimmt, antwortet das Obige mit „Hello World“, wenn er mit /the- übereinstimmt. Antwort: Dann wird gleichzeitig „42“ zurückgegeben. Wenn keines von beiden übereinstimmt, wird ein leeres 你也可以组合要求谓词,通过调用 事实上,在 方法引用 顺便说一句:到目前为止,我们已经编写了所有的处理函数作为内联的lambda表达式。虽然这在演示和短的例子中表现良好,但是不得不说这有一种会导致“混乱”的倾向,因为你要混合两种担忧:请求路由和请求处理。因此,我们要看看是否能够让事情变得更简洁。首先,我们创建一个包含处理代码的类: 注意,两个方法都有一个兼容了处理函数的标志。这允许我们使用方法引用: FilterFunction 由路由函数映射的路径可以通过调用RouterFunction.filter(FilterFunction 需要注意的是,要不要调用下一个处理程序是可选的。这在安全和缓存方案中非常有用(如只在用户有足够权限的时候调用 由于 使用注解,相似的功能可以用@ControllerAdvice和/或ServletFilter来实现。 所有这一切都很好,但有一件事忘了:我们如何才能在实际的HTTP服务器中运行这些函数呢?答案勿庸置疑是通过调用另一个函数。你可以通过使用RouterFunctions.toHttpHandler()将路由函数转换成HttpHandler。 有一点要注意的是,上面的代码不依赖于Spring应用程序上下文。就像 还要注意的是,你也可以转换路由函数为HandlerMapping,以便它可以在 让我通过简短的总结来得出结论: 处理函数通过返回响应处理请求。 路由函数路由到处理函数,并且可以与其他路由函数组合。 路由函数可以通过过滤器进行过滤。 路由函数可以在响应的web运行时中运行。 Das obige ist der detaillierte Inhalt vonNeue Funktionen von Spring5 – detaillierte Codebeispiele für funktionale Web-Frameworks. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!RouterFunction<T>
(d. h. FunctionRouterFunction<String> helloWorldRoute =
request -> {
if (request.path().equals("/hello-world")) {
return Optional.of(r -> Response.ok().body(fromObject("Hello World")));
} else {
return Optional.empty();
}
};
RouterFunction<String> helloWorldRoute =
RouterFunctions.route(request -> request.path().equals("/hello-world"),
request -> Response.ok().body(fromObject("Hello World")));
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")))
.and(route(path("/the-answer"),
request -> Response.ok().body(fromObject("42"))));
Optional
zurückgegeben. Beachten Sie, dass die kombinierten Routing-Funktionen sequentiell ausgeführt werden. Daher ist es sinnvoll, die generische Funktion vor der konkreten Funktion zu platzieren. 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))));
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));
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的原因。在处理程序类中,两种方法都返回ResponseString
响应主体。我们可以通过使用RouterFunction.andSame()来代替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));
});
运行服务端
HttpHandler
是引进到Spring 5.0 M1的一个响应抽象:它允许你运行在各种响应运行时上:Reactor Netty、RxNetty、Servlet 3.1+,和Undertow。在这个例子中,我们已经表明了在Reactor Netty中运行route
是怎么样的。对于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();
JdbcTemplate
和其他Spring实用工具类,使用应用程序上下文是可选的:你可以在上下文中接通处理程序和路由函数,但它不是必需的。DispatcherHandler
中运行(可能需要有响应的@Controllers)。结论