[JAVA] Probieren Sie Spring WebFlux aus (hauptsächlich Router-Funktionen).

Überblick

Beim Java Day Tokyo 2017 hörte ich Reaktive Webanwendung von Spring Framework 5.0 und wurde etwas nervös, also Spring 5 Ich habe versucht, Spring WebFlux von hinzuzufügen.

Klicken Sie hier für Referenzmaterialien. First Spring WebFlux (Teil 1 - Versuchen Sie Spring WebFlux) Der Autor ist Toshiaki Maki, der mit dem Sprecher identisch ist. (Tatsächlich sagte er in der Sitzung etwas wie "Ich schreibe ein Blog. Wenn Sie es also lesen, können Sie es versuchen.")


Bis Sie die App vorerst starten

Diesmal habe ich Spring Boot 2.0.0 M1 verwendet. (Weil Spring Boot Spring 5 ab 2.0.0 unterstützt)

Erstellen Sie es mit Spring Initializer. Stellen Sie die Spring Boot-Version auf 2.0.0 M1 ein und fügen Sie "Reactive Web" hinzu. image.png (Übrigens Aktuator wird zum Zeitpunkt von M1 nicht unterstützt. Entschuldigung)

Also habe ich damit angefangen und bestätigt, dass Netty gestartet ist. Es ist vorerst in Ordnung. Unten finden Sie einen Auszug aus dem Startprotokoll.

2017-05-20 14:14:17.447  INFO 4880 --- [  restartedMain] o.s.w.r.r.m.a.ControllerMethodResolver   : Looking for @ControllerAdvice: org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext@30361c29: startup date [Sat May 20 14:14:16 JST 2017]; root of context hierarchy
2017-05-20 14:14:17.916  INFO 4880 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2017-05-20 14:14:17.944  INFO 4880 --- [  restartedMain] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-05-20 14:14:18.241  INFO 4880 --- [  restartedMain] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 8080
2017-05-20 14:14:18.246  INFO 4880 --- [  restartedMain] itaka.demo.WebfluxSampleApplication      : Started WebfluxSampleApplication in 2.181 seconds (JVM running for 3.946)

Routing-Definition mit Router-Funktionen

Wenn in WebFlux eine "@ Bean" wie "RouterFunction " vorhanden ist, wird diese als Routing-Definition behandelt. Wenn Sie es also in die Klasse schreiben, die für den Komponentenscan vorgesehen ist, sieht es überall gut aus.

//Ich habe einige statische Importe. Weitere Informationen finden Sie im Referenzmaterial.
@Bean
public RouterFunction<ServerResponse> routes() {
    return route(GET("/"), req -> ok().body(Flux.just("Hello", "WebFlux"), String.class));
}

Wenn ich es starte und auf http: // localhost: 8080 / zugreife, wird" Hello WebFlux "zurückgegeben. das ist alles.

Wie gehe ich mit PathVariable um?

  1. Beschreiben Sie das Argument von RequestPredicate (GET oder POST) wie zuvor als" {var} "
  2. Holen Sie sich mit .pathVariable () von ServerRequest

Sie können mit dem Verfahren von gehen.

@Bean
public RouterFunction<ServerResponse> routes() {
    return RouterFunctions
            .route(GET("/hello/{name}"), req ->
                    ok().body(Flux.just("Hello", req.pathVariable("name")), String.class));
}

Routing-Priorität

Was passiert, wenn Sie sowohl das Routing der Pfadvariablen oben als auch das Routing der festen Pfaddefinition "/ hello / hoge" definieren? Aus der Schlussfolgerung geht hervor, dass dem ersten die Priorität eingeräumt wird. einfach.

Wenn Sie zuerst das Routing der Pfadvariablen schreiben

RouterFunctions
 .route(GET("/hello/{name}"), req -> ...)
 .AndRroute(GET("/hello/hoge"), req -> ...);

-> Pfadvariable hat Priorität.

Wenn das Routing der festen Pfaddefinition zuerst geschrieben wird

RouterFunctions
 .route(GET("/hello/hoge"), req -> ...)
 .AndRroute(GET("/hello/{name}"), req -> ...);

-> Feste Pfaddefinition hat Priorität.

Wenn die Anzahl der Routing-Definitionen zunimmt

Wenn es um den Maßstab des Beispiels geht, gibt es kein Problem, selbst wenn Sie die Routendefinition wie oben beschrieben verbinden. In der tatsächlichen Anwendung bedeutet dies jedoch, dass alle Zuordnungen in einer Controller-Klasse __ beschrieben sind Es ist also nicht gut. Daher können Sie ein wenig ordentlich schreiben, indem Sie die folgenden Schritte ausführen.

  1. Erstellen Sie eine Methode, die "RouterFunction " zurückgibt, und fügen Sie sie in eine andere Klasse ein
  2. Rufen Sie Methode 1 in der ursprünglich definierten Methode "@ Bean" auf

HelloRouter.java


@Component
public class HelloRouter {

    private static final String PATH = "/hello";

    public RouterFunction<ServerResponse> route() {
        return RouterFunctions
                .route(GET(PATH), this::hello)
                .andRoute(GET(PATH + "/hoge"), this::helloHoge)
                .andRoute(GET(PATH + "/{name}"), this::helloName);
    }

    private Mono<ServerResponse> hello(ServerRequest req) {
        return ok().body(Flux.just("Hello", "WebFlux"), String.class);
    }

    private Mono<ServerResponse> helloName(ServerRequest req) {
        return ok().body(Flux.just("Hello", req.pathVariable("name")), String.class);
    }

    private Mono<ServerResponse> helloHoge(ServerRequest req) {
        return ok().body(Flux.just("Hoge", "Hoge"), String.class);
    }
}

Hallo Router Route()Anruf


@Bean
public RouterFunction<ServerResponse> routes(HelloRouter helloRouter) {
    return helloRouter.route();
}

Darüber hinaus können mehrere "RouterFunction " durch eine Methodenkette mit ".and" verbunden werden.

.and()Reiben mehrerer Routerfunktionen mit


//In StreamRouter"/strem"Es wird davon ausgegangen, dass das Routing von
@Bean
public RouterFunction<ServerResponse> routes(HelloRouter helloRouter, StreamRouter streamRouter) {
    return helloRouter.route()
            .and(streamRouter.route());
}

Dies legt das folgende Routing fest.

(Ergänzung) Verbesserung durch RouterFunctions.nest ()

Kommentare Versuchen Sie also, den obigen Code mit "nest ()" weiter umzuschreiben. (Danke an Maki!)

RouterFunctions.nest()Hierarchische Routing-Definition


public RouterFunction<ServerResponse> routes() {
    return nest(path("/hello"),
            route(GET("/"), this::hello)
                .andRoute(GET("/hoge"), this::helloHoge)
                .andRoute(GET("/{name}"), this::helloName));
}

Übrigens habe ich auch statisch RouterFunctions.route () importiert, also war es sehr erfrischend! Da Sie den Routing-Inhalt nur anhand des Router-Funktionsteils verstehen können, ist dies auch ein besserer Ort als die Definition in "@ Controller".


Spielen Sie mit Infinite Stream

Das in ".body ()" von ServerResponse festgelegte "Flux" ist eine Klasse, die Publisher of Reactive Stream implementiert, und es scheint, dass Sie einen Creative Stream mit einem guten Gefühl realisieren können.

Lassen Sie uns als konkretes Beispiel etwas implementieren, das "beim Zugriff endlos zurückgegeben und die Zahlen hochgezählt werden". Die Methode ist wie folgt.

  1. Erstellen Sie einen unendlichen Stream
  2. Machen Sie Flux aus 1 Stream
  3. Stellen Sie 2 Flux als Körper ein
@Bean
public RouterFunction<ServerResponse> routes() {
    Stream<Integer> stream = Stream.iterate(0, i -> i + 1);
    Flux<Integer> flux = Flux.fromStream(stream).delayElements(Duration.ofSeconds(1));

    return RouterFunctions
            .route(GET("/stream"), req ->
                    ok().contentType(MediaType.APPLICATION_STREAM_JSON).body(flux, Integer.class));
}

Wenn Sie jetzt auf "http: // localhost: 8080 / stream" zugreifen, fließen die Zahlen endlos. Da es jedoch so ist, fließt es mit großer Dynamik. Im obigen Beispiel wird ".delayElements ()" verwendet, um es mit einem Intervall fließen zu lassen.


Zusammenfassung

Während ich "try WebFlux" sagte, experimentierte ich mehr als die Hälfte mit Router-Funktionen. Um "Web Flux ist erstaunlich" zu erleben, scheint es besser, eine Anwendung zu erstellen, die als kompliziert oder "superschnell, weil sie nicht blockiert" verstanden werden kann.

Router-Funktionen waren aufregender als beim Anhören der Java Day Tokyo-Sitzung. Diesmal habe ich nur "GET" verwendet, aber natürlich können auch andere HttpMethods verwendet werden, und komplexe Routing-Bedingungen können mit ".and ()", ".or ()", ".negate ()" usw. erstellt werden. Es sieht lustig aus, weil du es auch kannst. Im Moment (Spring Boot 2.0.0) scheint es jedoch nicht mit "@ Controller" koexistieren zu können, so dass es ein wenig schwierig ist, es auf vorhandene Anwendungen anzuwenden (alle müssen neu geschrieben werden). Wenn Sie es tun, sollten Sie es mit einer neuen Anwendung tun.

Der Code von dem, was ich dieses Mal versucht habe, ist wie folgt. (Da ich beim Schreiben überarbeitet habe, gibt es eine leichte Abweichung vom Code in diesem Artikel.) IsaoTakahashi/webflux-sample - GitHub

Recommended Posts

Probieren Sie Spring WebFlux aus (hauptsächlich Router-Funktionen).
Probieren Sie das Spring WebFlux-Tutorial aus
Versuchen Sie es mit Spring JDBC
Versuchen Sie Spring Boot von 0 bis 100.
Versuchen Sie, eine WebFlux-Sitzung zu implementieren
Versuchen Sie es mit Spring Boot Security
Versuchen Sie, einen WebFlux-Filter zu implementieren
Versuchen Sie Spring Boot auf dem Mac