I was a little nervous when I heard Reactive Web Application by Spring Framework 5.0 at Java Day Tokyo 2017, so Spring 5 I tried Spring WebFlux added from.
Click here for reference materials. First Spring WebFlux (Part 1-Try Spring WebFlux) The author is Toshiaki Maki, who is the same as the speaker. (In fact, in the session, he said something like "I'm writing a blog, so if you read it, you can try it.")
This time I used Spring Boot 2.0.0 M1. (Because Spring Boot supports Spring 5 from 2.0.0)
Create it with Spring Initializer. Set Spring Boot version to 2.0.0 M1 and add "Reactive Web". (By the way, actuator is not supported at the time of M1. Sorry)
So, I started it and confirmed that Netty started up. It's OK for the time being. Below is an excerpt of the startup log.
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)
In WebFlux, if there is a @Bean
such asRouterFunction <ServerResponse>
, it will be treated as a routing definition.
So, if you write it in the class targeted for Component Scan, it looks good anywhere.
//I have some static imports, so please refer to the reference material for details.
@Bean
public RouterFunction<ServerResponse> routes() {
return route(GET("/"), req -> ok().body(Flux.just("Hello", "WebFlux"), String.class));
}
So, when I start it and access http: // localhost: 8080 /
, "Hello WebFlux" is returned.
that's all.
GET
or POST
) as before..pathVariable ()
of ServerRequestYou can go with the procedure of.
@Bean
public RouterFunction<ServerResponse> routes() {
return RouterFunctions
.route(GET("/hello/{name}"), req ->
ok().body(Flux.just("Hello", req.pathVariable("name")), String.class));
}
What happens if you define both the PathVariable routing above and the fixed path definition routing "/ hello / hoge"? From the conclusion, __ the first one is given priority __. simple.
RouterFunctions
.route(GET("/hello/{name}"), req -> ...)
.AndRroute(GET("/hello/hoge"), req -> ...);
-> Path Variable has priority.
RouterFunctions
.route(GET("/hello/hoge"), req -> ...)
.AndRroute(GET("/hello/{name}"), req -> ...);
-> Fixed path definition takes precedence.
If it is about the scale of the sample, there is no problem even if you connect the route definition as described above, but in the actual application, this means that all Mapping is described in one Controller class __ So it's not good. Therefore, you can write a little neatly by doing the following.
RouterFunction <ServerResponse>
and put it in another class@ Bean
defined methodHelloRouter.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);
}
}
HelloRouter route()Call
@Bean
public RouterFunction<ServerResponse> routes(HelloRouter helloRouter) {
return helloRouter.route();
}
Furthermore, multiple RouterFunction <ServerResponse>
can be connected by a method chain with .and
.
.and()Rubbing multiple Router Functions with
//In StreamRouter"/strem"It is assumed that the routing of
@Bean
public RouterFunction<ServerResponse> routes(HelloRouter helloRouter, StreamRouter streamRouter) {
return helloRouter.route()
.and(streamRouter.route());
}
This sets the following routing.
Comments, so try rewriting the above code further using nest ()
.
(Thanks to Maki!)
RouterFunctions.nest()Routing definition in a hierarchical manner
public RouterFunction<ServerResponse> routes() {
return nest(path("/hello"),
route(GET("/"), this::hello)
.andRoute(GET("/hoge"), this::helloHoge)
.andRoute(GET("/{name}"), this::helloName));
}
By the way, I also statically imported RouterFunctions.route ()
, so it was very refreshing!
Since you can understand the Routing contents only by looking at the Router Function part, this is also a better place than the definition in @Controller
.
The Flux
set in.body ()
of ServerResponse is a class that implements Publisher of Reactive Stream, and it seems that you can realize a Creative Stream with a good feeling.
As a concrete example, let's implement something that "when you access it, the numbers are returned endlessly and the numbers are counted up". The method is as follows.
@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));
}
Now, when you access http: // localhost: 8080 / stream
, the numbers will flow endlessly.
However, since it flows with great momentum as it is, in the above example, .delayElements ()
is used to make it flow with an interval.
While saying "try WebFlux", I ended up experimenting with Router Functions more than half. In order to experience "WebFlux is amazing", it seems better to create an application that can be understood as being complicated or "super fast because it is Non Blocking".
Router Functiuons was more exciting than when I was listening to the Java Day Tokyo session.
I used only GET
this time, but of course other HttpMethods can be used, and complex routing conditions can be created using.and ()
,.or ()
,.negate ()
, etc. It looks fun because you can also do it.
However, at the moment (Spring Boot 2.0.0), it seems that it cannot coexist with @Controller
, so it is a little difficult to apply it to existing applications (all must be rewritten).
If you do, you should do it with a new application.
The code of what I tried this time is as follows. (Since I refactored while writing, there is a slight deviation from the code in this article) IsaoTakahashi/webflux-sample - GitHub