文章目录
- WebFlux初次尝试
- 处理过程源码分析
SpringMvc通常是Servlet应用,因此,可能被当前线程阻塞。以远程调用为例,由于阻塞的缘故,导致Servlet容器使用较大的线程池处理请求。而Spring WebFlux通常是非阻塞的服务(同步/异步无法确定,Reactor默认同步,可改为异步),不会发生阻塞,因此该阻塞服务器可使用少量、固定大小的线程池处理请求。(非阻塞无非就是当前不阻塞,后续去回调。)
WebFlux初次尝试
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public RouterFunction<ServerResponse> routerFunction(){
return route(GET("/hello"),this::hello);
}
public Mono<ServerResponse> hello(ServerRequest serverRequest){
return ServerResponse.status(HttpStatus.OK)
.body(Mono.just("hello world"),String.class);
}
}
启动项目后,通过访问"localhost:8080/hello"就可以得到返回值"hello world"。
这个过程中,需要注意这五个类及其方法:
- 路由方法:RouterFunction#route
- 请求判定:RequestPredicate(类似于Predicate)
- 处理函数:HandlerFunction
- 请求接口:ServerRequest
- 响应接口:ServerResponse
处理过程源码分析
传入的请求被路由到有RouterFunction<T>的处理函数(即Function<Request, Optional<HandlerFunction<T>>)路由到处理函数,如果它匹配的话;否则就返回一个空的结果。路由方法与@RequestMapping注解的作用相似。但是,还有一个显著的区别:用注解时路由会被限制到注解的value所能表达的范围,处理这些方法的覆盖是困难的(除非用@profile注解);当用路由方法的时候,代码就在那里,可以轻松的覆盖或替换。
@Configuration
public class PersonRouter {
// solution
@Bean
public RouterFunction<ServerResponse> peopleRoutes(PersonHandler personHandler) {
return RouterFunctions.route(GET("/people/{id}").and(accept(APPLICATION_JSON)), personHandler::get)
.andRoute(GET("/people").and(accept(APPLICATION_JSON)), personHandler::all)
.andRoute(POST("/people").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::post)
.andRoute(PUT("/people/{id}").and(accept(APPLICATION_JSON)).and(contentType(APPLICATION_JSON)), personHandler::put)
.andRoute(DELETE("/people/{id}"), personHandler::delete)
.andRoute(GET("/people/country/{country}").and(accept(APPLICATION_JSON)), personHandler::getByCountry);
}
}
路由函数的组合 如果第一个RountHandler 匹配不成功,则进入的下一个RountHandler,匹配顺序按代码顺序执行。
静态引入RouterFunctions.route(),这样就可以用请求判断式(RequestPredicate) (即 Predicate<Request>)和处理方法(HandlerFunction)创建路由方法了,其java源码如下:
public static <T extends ServerResponse> RouterFunction<T> route(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
return new RouterFunctions.DefaultRouterFunction(predicate, handlerFunction);
}
再来看看Predicate的源码:
@FunctionalInterface
public interface RequestPredicate {
boolean test(ServerRequest var1);
...default method...
}
可以知道RequestPredicate其实就是一个返回布尔值的方法参数。如果判断式判断成功则返回处理方法,否则返回空结果。
这一新框架的起点是HandlerFunction<T>,基本上是Function<Request, Response<T>>,其源码如下:
@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
Mono<T> handle(ServerRequest var1);
}
可以看到,HandlerFunction接受ServerRequest类型的参数,返回Mono包装的ServerResponse类型的返回值。一个简单的“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> )。没有副作用有很多好处:易于测试,编写和优化。
ServerRequest并且ServerResponse接口可以提供JDK-8(Lambda)对底层HTTP消息的友好访问。
函数式端点组件请求流程处理:
注解驱动组件流程请求处理: