第10章 Spring WebFlux路由模式解读 01 Spring WebFlux函数式路由模式解读
知秋z:Java编程方法论-Reactor-Netty与Spring WebFlux解读 整体简介与导读
第10章 Spring WebFlux路由模式解读
Spring WebFlux包含一个轻量级的函数式编程模型,其中定义的函数可以对请求进行路由处理。请求也可以通过基于注解形式的路由模式进行处理。由9.6节可知,基于注解形式的Controller路由处理和基于函数式路由处理的这两种模式可以在同一个Reactive Core的基础上同时运行。那么,我们要做的就是对多种路由模式进行统一模型的适配设计。
要对多种模式进行适配设计,首先要做的就是根据具体情况来判断所传入的处理类是由哪种模式所支持的,这里不需要做得非常复杂,只需要将模式支持的类设计好(即设计一个适配器),并将其集中到一个集合中,然后迭代、遍历并对所传入的处理类进行判断即可。如果支持,就将请求和对应的处理类放在适配器的handle方法中执行相应模式下的代码逻辑:
//org.springframework.web.reactive.HandlerAdapter
public interface HandlerAdapter {
boolean supports(Object handler);
Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler);
//org.springframework.web.reactive.DispatcherHandler#invokeHandler
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
if (this.handlerAdapters != null) {
for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
if (handlerAdapter.supports(handler)) {
return handlerAdapter.handle(exchange, handler);
return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}
10.1 Spring WebFlux函数式路由模式解读
其实前面已经讲解过Reactor Netty提供的HttpServerRoutes的设计与实现,现在我们应该可以感觉到,其功能远远不能满足我们对Web上层进行复杂处理的需求。每一个请求都要经历过滤链的处理,若产生异常,还要进行异常链的处理,以及正常地路由到相应的目标处理handler上并进行逻辑业务的处理,而对返回结果的处理,整个过程单靠HttpRouteHandler与DefaultHttpServerRoutes是不行的。于是,通过对WebHandler进行设定来专门处理此类业务,其综合表现为通过WebHttpHandlerBuilder中的build方法返回的HttpHandler实现类接入ReactorHttpHandlerAdapter,然后到DispatcherHandler完成调度设计,完成服务器从Web层到Reactor Netty的组装。
本章我们主要对Spring WebFlux函数式路由模式进行解读。
10.1.1 对HandlerFunction的设定
而对于HttpHandler与WebHandler这些上层接口建筑来说,我们主要关心对请求的处理过程,结合我们在DispatcherHandler#handle中看到的flatMap(handler -> invokeHandler(exchange, handler)),对于处理请求后得到的结果,要满足DispatcherHandler#handle中flatMap(result -> handleResult(exchange, result))对result类型的要求,即Mono<HandlerResult>,在执行invokeHandler方法的时候,需要将得到的ServerResponse封装为一个Mono<ServerResponse>,再通过响应式的衔接调用,将ServerResponse封装为一个HandlerResult对象,最后得到一个Mono<HandlerResult>对象。
而我们最关心的是对于请求的处理,并得到处理后的结果Mono<ServerResponse>,其他的都可以封装起来,由此,可以设定一个函数式接口来对接我们的业务处理类org.springframework.web.reactive.function.server.HandlerFunction:
//org.springframework.web.reactive.function.server.HandlerFunction
@FunctionalInterface
public interface HandlerFunction<T extends ServerResponse> {
* Handle the given request.
* @param request the request to handle
* @return the response
Mono<T> handle(ServerRequest request);
于是,具体的业务处理类可进行如下实现:
public class UserHandler {
private final UserRepository userRepository;
public UserHandler(UserRepository userRepository) {
this.userRepository = userRepository;
public Mono<ServerResponse> getAllUsers(ServerRequest request) {
Flux<User> users = this.userRepository.getAllUsers();
return ServerResponse.ok().contentType(APPLICATION_JSON).body(users, User.class);
}
结合我们在Spring MVC中@Controller所注解的业务处理类实现可知,请求对应的都是Controller类中的某个方法,所以,在相应业务的处理类中,针对HandlerFunction函数式接口在方法级别进行实现,如上面的源码所示,接下来该怎么使用呢?先来看一个Demo:
UserRepository repository = ...
UserHandler handler = new UserHandler(repository);
route(GET("/{id}"), handler::getAllUsers)
在这里,route方法可以定义为route(RequestPredicate predicate, HandlerFunction<T> handlerFunction),第1个参数为访问条件,第2个参数是handlerFunction的一个实现,而当请求条件匹配时,就可以执行此handlerFunction函数动作了。也就是说,其返回值应该是RouterFunction<T>,其中的T应该和HandlerFunction的接口定义一致,为<T extends ServerResponse>,下面来看看该方法的具体实现:
//org.springframework.web.reactive.function.server.RouterFunctions#route
public static <T extends ServerResponse> RouterFunction<T> route(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
return new DefaultRouterFunction<>(predicate, handlerFunction);
}
对于函数式表达式,我之前讲过它其实就是一个动作,也可以认为它是一种能力,这种能力比较单一,而且使用函数式接口往往是为了对外适配,即只认类型,于是我们在使用时可以将此函数式接口的动作设定为自己定义的类中的一个字段,在外围使用时可提供该功能函数的自定义实现,这有点类似于抽象类,但更灵活,允许我们做类似于UserHandler的实现。用数学思维解释就是 z = g ( f ( x )),其中的 g (...)根本不用关心 f ( x )的具体实现,它只执行自己的一套逻辑,而将 f ( x )交由使用者来实现即可,这就是高阶函数设计的玩法。于是,我们也可以做如下方式的实现:
HandlerFunction<ServerResponse> helloWorld =
request -> ServerResponse.ok().body(fromObject("Hello World"));
route(GET("/{id}"), helloWorld)
10.1.2 RouterFunction的设计
在确定了HandlerFunction的设定之后,自然就能想到如何将请求交到HandlerFunction手里,按照单一功能原则,这里设计了一个路由功能的函数式动作RouterFunction:
//org.springframework.web.reactive.function.server.RouterFunction
@FunctionalInterface
public interface RouterFunction<T extends ServerResponse> {
Mono<HandlerFunction<T>> route(ServerRequest request);
//其余都为默认实现方法
}
然后,逻辑也就清楚了。接着,我们将HandlerFunction与RouterFunction结合,这样就可以完成请求的处理工作,即在RouterFunction内对请求进行路由判断,若符合条件,就由当前的HandlerFunction实现进行处理。与Reactor Netty中HttpRouteHandler的设计一样,我们要将一个处理路径的实现与该路径的判断条件绑定起来,于是需要对HandlerFunction进行功能增强,将具体的HandlerFunction实现与针对请求进行条件判断而设定的RequestPredicate接口实现作为该增强类的字段,然后在核心方法route(..)中进行逻辑整合,相关定义如下:
//org.springframework.web.reactive.function.server.RequestPredicate
@FunctionalInterface
public interface RequestPredicate {
* Evaluate this predicate on the given request.
* @param request the request to match against
* @return {@code true} if the request matches the predicate; {@code false} otherwise
boolean test(ServerRequest request);
//其余都为默认实现方法
//org.springframework.web.reactive.function.server.RouterFunctions.DefaultRouterFunction
private static final class DefaultRouterFunction<T extends ServerResponse>
extends AbstractRouterFunction<T> {
private final RequestPredicate predicate;
private final HandlerFunction<T> handlerFunction;
public DefaultRouterFunction(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
Assert.notNull(predicate, "Predicate must not be null");
Assert.notNull(handlerFunction, "HandlerFunction must not be null");
this.predicate = predicate;
this.handlerFunction = handlerFunction;
// 重点
@Override
public Mono<HandlerFunction<T>> route(ServerRequest request) {
if (this.predicate.test(request)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Predicate \"%s\" matches against \"%s\"", this.predicate, request));
return Mono.just(this.handlerFunction);
else {
return Mono.empty();
@Override
public void accept(Visitor visitor) {
visitor.route(this.predicate, this.handlerFunction);
//org.springframework.web.reactive.function.server.RouterFunctions.AbstractRouterFunction
private abstract static class AbstractRouterFunction<T extends ServerResponse> implements RouterFunction<T> {
@Override
public String toString() {
ToStringVisitor visitor = new ToStringVisitor();
accept(visitor);
return visitor.toString();
}
此处的accept是针对RouterFunction这个类的toString方法设定的,只为打印出详细的相关信息,另外这里就不对ToStringVisitor进行深入探讨了。还有一些其他特殊功能的RouterFunction,也是按照上面的设计思路设计的,也不一一介绍了。
10.1.3 RouterFunctions的设计
现在剩下的就是我们该怎样使用DefaultRouterFunction来构造出一系列的访问路径,映射处理链,结合10.1.2中展示的DefaultRouterFunction的route方法具体实现。如果路径不匹配,就会返回Mono.empty,那么我们是不是可以通过reactor.core.publisher.Mono#switchIfEmpty操作使用两个路径处理类构造出一个调用链,当前不支持一个路径处理环节的时候就进入下一个环节,以此类推,那么我们可以设计一个组合包装类,对外其也是一个RouterFunction,这有点类似于链表的设计,里面放置了两个RouterFunction具体实现,在route方法中由这两个实现执行具体的逻辑,只不过当第一个具体实现处理不了时,就通过switchIfEmpty交由第二个具体实现来执行判断。依此类推,因为下一个RouterFunction也可能是这个组合包装类,于是有下面的源码实现:
//org.springframework.web.reactive.function.server.RouterFunctions.SameComposedRouterFunction
static final class SameComposedRouterFunction<T extends ServerResponse> extends AbstractRouterFunction<T> {
private final RouterFunction<T> first;
private final RouterFunction<T> second;
public SameComposedRouterFunction(RouterFunction<T> first, RouterFunction<T> second) {
this.first = first;
this.second = second;
@Override
public Mono<HandlerFunction<T>> route(ServerRequest request) {
return this.first.route(request)
.switchIfEmpty(Mono.defer(() -> this.second.route(request)));
@Override
public void accept(Visitor visitor) {
this.first.accept(visitor);
this.second.accept(visitor);
}
因为只是将多个RouterFunction进行了衔接组合,和被用来衔接的RouterFunction的具体实现并无关联,更多是对RouterFunction的功能性增强,所以就此功能,我们完全可以将其放在RouterFunction自己的接口中,作为一个默认方法:
//org.springframework.web.reactive.function.server.RouterFunction#and
default RouterFunction<T> and(RouterFunction<T> other) {
return new RouterFunctions.SameComposedRouterFunction<>(this, other);
}
再结合前面讲解过的RouterFunctions#route实现,我们也可以在此对它进行整合封装,让接口更具语义性,功能也更集中:
//org.springframework.web.reactive.function.server.RouterFunction#andRoute
default RouterFunction<T> andRoute(RequestPredicate predicate, HandlerFunction<T> handlerFunction) {
return and(RouterFunctions.route(predicate, handlerFunction));
}
完成以上这些设计,我们把得到的RouterFunction作为一个bean放到Spring容器中,如下面这个代码片段示例所示:
@Bean
public RouterFunction<ServerResponse> route(QuoteHandler quoteHandler) {
return RouterFunctions
.route(GET("/hello").and(accept(TEXT_PLAIN)), quoteHandler::hello)
.andRoute(POST("/echo").and(accept(TEXT_PLAIN).and(contentType(TEXT_PLAIN))), quoteHandler::echo)
.andRoute(GET("/quotes").and(accept(APPLICATION_JSON)), quoteHandler::fetchQuotes)
.andRoute(GET("/quotes").and(accept(APPLICATION_STREAM_JSON)), quoteHandler::streamQuotes);
}
结合前面讲解过的DispatcherHandler构造器中的initStrategies方法的细节,为了方便回顾,这里再来看该方法的源码,如下所示,在初始化DispatcherHandler时就要从Spring容器上下文中获取HandlerMapping与HandlerAdapter实例,因此这两个接口的实现类会在WebFluxConfigurationSupport这个类中进行配置,方便初始化DispatcherHandler时获取它们。
//org.springframework.web.reactive.DispatcherHandler#initStrategies
protected void initStrategies(ApplicationContext context) {
Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerMapping.class, true, false);
ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
AnnotationAwareOrderComparator.sort(mappings);
this.handlerMappings = Collections.unmodifiableList(mappings);
Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerAdapter.class, true, false);
this.handlerAdapters = new ArrayList<>(adapterBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
context, HandlerResultHandler.class, true, false);
this.resultHandlers = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(this.resultHandlers);
}
既然我们要由HandlerMapping跟据request请求来获取前面定义的HandlerFunction,那就免不了与RouterFunction一起配合做这件事,在HandlerMapping的实现类中放置一个RouterFunction字段,在初始化HandlerMapping时加载容器中放置的RouterFunction类型的bean实例(如前面的示例代码片段所示)。在这里,我们可以借助Spring中的BeanFactory设定的bean初始化周期方法来做一些切入性操作,即在初始化bean之后立马执行一个方法,也就是实现一个InitializingBean接口:
//org.springframework.beans.factory.InitializingBean
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
借助这个afterPropertiesSet方法,我们就可以获取容器中的RouterFunction类型的bean(如果在Spring容器中有定义多个该RouterFunction类型bean,就将它们先放到一个集合中),并,并通过RouterFunction的andOther方法,将这些bean衔接起来构造出一条RouterFunction路由实现:
//org.springframework.web.reactive.function.server.support.RouterFunctionMapping
public class RouterFunctionMapping extends AbstractHandlerMapping implements InitializingBean {
@Nullable
private RouterFunction<?> routerFunction;
@Nullable
public RouterFunction<?> getRouterFunction() {
return this.routerFunction;
@Override
public void afterPropertiesSet() throws Exception {
if (CollectionUtils.isEmpty(this.messageReaders)) {
ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create();
this.messageReaders = codecConfigurer.getReaders();
if (this.routerFunction == null) {
initRouterFunctions();
protected void initRouterFunctions() {
List<RouterFunction<?>> routerFunctions = routerFunctions();
this.routerFunction = routerFunctions.stream().reduce(RouterFunction::andOther).orElse(null);
logRouterFunctions(routerFunctions);
private List<RouterFunction<?>> routerFunctions() {
List<RouterFunction<?>> functions = obtainApplicationContext()
.getBeanProvider(RouterFunction.class)
.orderedStream()
.map(router -> (RouterFunction<?>)router)
.collect(Collectors.toList());
return (!CollectionUtils.isEmpty(functions) ? functions : Collections.emptyList());
}
使用上面routerFunctions中的orderedStream表示如果我们的bean有加@Order注解,就按优先级来进行路由拼接。在进行路由拼接的时候,可以看到在initRouterFunctions方法中使用前面所提到的RouterFunction的andOther来应对不同响应类型下的RouterFunction的衔接(比如衔接的这个链条中,当请求到来,且某个RouterFunction对象执行route方法得到的结果有可能是Mono#empty),以此保证程序的健壮性,可对比前面讲过的RouterFunction#and,具体实现如下:
//RouterFunctions.DifferentComposedRouterFunction
static final class DifferentComposedRouterFunction extends AbstractRouterFunction<ServerResponse> {
private final RouterFunction<?> first;
private final RouterFunction<?> second;
public DifferentComposedRouterFunction(RouterFunction<?> first, RouterFunction<?> second) {
this.first = first;
this.second = second;
@Override
public Mono<HandlerFunction<ServerResponse>> route(ServerRequest request) {
return this.first.route(request)
.map(this::cast)
.switchIfEmpty(Mono.defer(() -> this.second.route(request).map(this::cast)));
@SuppressWarnings("unchecked")
private <T extends ServerResponse> HandlerFunction<T> cast(HandlerFunction<?> handlerFunction) {
return (HandlerFunction<T>) handlerFunction;
@Override
public void accept(Visitor visitor) {
this.first.accept(visitor);
this.second.accept(visitor);
}
可以看到,在route方法中,首先通过map操作对获得的handlerFunction进行了类型强转,用于保证后续链式操作中类型传递的正确性,接着通过switchIfEmpty操作来对Mono#empty这个可能的结果进行判断和衔接控制,最后通过下面这个Demo来看看如何使用RouterFunction的andOther方法:
@Test
public void andOther() {
HandlerFunction<ServerResponse> handlerFunction =
request -> ServerResponse.ok().body(fromObject("42"));
RouterFunction<?> routerFunction1 = request -> Mono.empty();
RouterFunction<ServerResponse> routerFunction2 =
request -> Mono.just(handlerFunction);
RouterFunction<?> result = routerFunction1.andOther(routerFunction2);
assertThat(result).isNotNull();
MockServerRequest request = MockServerRequest.builder().build();
Mono<? extends HandlerFunction<?>> resultHandlerFunction = result.route(request);
StepVerifier.create(resultHandlerFunction)
.expectNextMatches(o -> o.equals(handlerFunction))
.expectComplete()
.verify();
}
最后,在org.springframework.web.reactive.config.WebFluxConfigurationSupport中对RouterFunctionMapping进行配置:
//org.springframework.web.reactive.config.WebFluxConfigurationSupport#routerFunctionMapping
@Bean
public RouterFunctionMapping routerFunctionMapping(ServerCodecConfigurer serverCodecConfigurer) {
RouterFunctionMapping mapping = createRouterFunctionMapping();
mapping.setOrder(-1); // go before RequestMappingHandlerMapping
mapping.setMessageReaders(serverCodecConfigurer.getReaders());
mapping.setCorsConfigurations(getCorsConfigurations());
return mapping;
}
从mapping.setOrder(-1)的设定来看,它比基于注解形式的路由处理方式优先级高。接着,再回到DispatcherHandler中,我们还要往容器中放入HandlerAdapter具体实例,来对多种路由处理方式对应的处理器handler做类型判断支持。如果支持,可以结合exchange和对应的handler来对请求进行处理,下面来看看HandlerFunctionAdapter的具体实现:
//org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter
public class HandlerFunctionAdapter implements HandlerAdapter {
private static final MethodParameter HANDLER_FUNCTION_RETURN_TYPE;
static {
try {
Method method = HandlerFunction.class.getMethod("handle", ServerRequest.class);
HANDLER_FUNCTION_RETURN_TYPE = new MethodParameter(method, -1);
catch (NoSuchMethodException ex) {
throw new IllegalStateException(ex);
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerFunction;
@Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
HandlerFunction<?> handlerFunction = (HandlerFunction<?>) handler;
ServerRequest request = exchange.getRequiredAttribute(RouterFunctions.REQUEST_ATTRIBUTE);
return handlerFunction.handle(request)
.map(response -> new HandlerResult(handlerFunction, response, HANDLER_FUNCTION_RETURN_TYPE));
}
同样,我们将HandlerFunctionAdapter通过WebFluxConfigurationSupport放置到Spring容器中,以方便在DispatcherHandler中进行查找和使用:
//org.springframework.web.reactive.config.WebFluxConfigurationSupport#handlerFunctionAdapter
@Bean
public HandlerFunctionAdapter handlerFunctionAdapter() {
return new HandlerFunctionAdapter();
}
这样,除了对响应结果进行了处理,我们已经完成了从请求接入到处理的整个过程的组装和融合。关于响应结果的处理,后面还会再次接触。
1. RouterFunctions对接HttpHandler的自有实现
完成了上面提到的设计之后,我们来想想,有没有一种方式可以绕过基于兼容注解路由的这一套适配实现,以降低性能开销(因为我们可以很确定自己只使用函数式路由,在初始化容器时没必要加入这些用不到的步骤)。那么回想整个过程,归根结底,对于请求的处理,还是要放在HttpHandler和WebHandler上,于是就可以设计一个对接类RouterFunctions来承上启下,这也方便开发者将自己所实现的逻辑轻松挂载到HttpHandler上,也正因如此,我们可以将RouterFunctions作为开发者切入Spring WebFlux的中心入口。回到上面提到的在RouterFunctions#route中使用DefaultRouterFunction。在对接HttpHandler时,首先对接比较上层的WebHandler,下面回顾一下WebHandler接口的定义:
public interface WebHandler {
Mono<Void> handle(ServerWebExchange exchange);
}
既然我们要返回一个WebHandler对象,即需要对它的handle方法进行实现,那么我们可以通过exchange构造出一个ServerRequest对象,并将此对象加入exchange管理的attributes属性中。接下来的代码逻辑就是将这个请求交给routerFunction.route执行,在这里要对没有可处理handlerFunction的情况进行判断并调用defaultIfEmpty进行处理,同时,要对相应的处理过程进行异常封装,整个过程如下面的源码所示,需要重点关注RouterFunctionWebHandler的handle方法:
//org.springframework.web.reactive.function.server.RouterFunctions#toWebHandler
public static WebHandler toWebHandler(RouterFunction<?> routerFunction) {
return toWebHandler(routerFunction, HandlerStrategies.withDefaults());
public static WebHandler toWebHandler(RouterFunction<?> routerFunction, HandlerStrategies strategies) {
Assert.notNull(routerFunction, "RouterFunction must not be null");
Assert.notNull(strategies, "HandlerStrategies must not be null");
return new RouterFunctionWebHandler(strategies, routerFunction);
private static class RouterFunctionWebHandler implements WebHandler {
private static final HandlerFunction<ServerResponse> NOT_FOUND_HANDLER =
request -> ServerResponse.notFound().build();
private final HandlerStrategies strategies;
private final RouterFunction<?> routerFunction;
public RouterFunctionWebHandler(HandlerStrategies strategies, RouterFunction<?> routerFunction) {
this.strategies = strategies;
this.routerFunction = routerFunction;
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
return Mono.defer(() -> {
ServerRequest request = new DefaultServerRequest(exchange, this.strategies.messageReaders());
addAttributes(exchange, request);
return this.routerFunction.route(request)
.defaultIfEmpty(notFound())
.flatMap(handlerFunction -> wrapException(() -> handlerFunction.handle(request)))
.flatMap(response -> wrapException(() -> response.writeTo(exchange,
new HandlerStrategiesResponseContext(this.strategies))));
private void addAttributes(ServerWebExchange exchange, ServerRequest request) {
Map<String, Object> attributes = exchange.getAttributes();
attributes.put(REQUEST_ATTRIBUTE, request);
@SuppressWarnings("unchecked")
private static <T extends ServerResponse> HandlerFunction<T> notFound() {
return (HandlerFunction<T>) NOT_FOUND_HANDLER;
private static <T> Mono<T> wrapException(Supplier<Mono<T>> supplier) {
try {
return supplier.get();
catch (Throwable ex) {
return Mono.error(ex);
}
结合我们在Reactor Netty相关内容中学到的知识,Spring WebFlux和Reactor Netty的衔接点在org.springframework.http.server.reactive.ReactorHttpHandlerAdapter类中,这个类中有一个HttpHandler字段。于是,在获得这个WebHandler对象之后,我们就可以通过WebHttpHandlerBuilder很容易地创建出一个HttpHandler对象:
//org.springframework.web.reactive.function.server.RouterFunctions#toHttpHandler
public static HttpHandler toHttpHandler(RouterFunction<?> routerFunction) {
return toHttpHandler(routerFunction, HandlerStrategies.withDefaults());
public static HttpHandler toHttpHandler(RouterFunction<?> routerFunction, HandlerStrategies strategies) {
WebHandler webHandler = toWebHandler(routerFunction, strategies);
return WebHttpHandlerBuilder.webHandler(webHandler)
.filters(filters -> filters.addAll(strategies.webFilters()))
.exceptionHandlers(handlers -> handlers.addAll(strategies.exceptionHandlers()))
.localeContextResolver(strategies.localeContextResolver())
.build();
}
可以看到,在toWebHandler方法内,将前面涉及过的过滤器、异常处理器进行了组合。关于加密/解密的相关内容,可以参考HandlerStrategies.withDefaults的实现,这里就不做过多解释了。至此可以说,我们已经从Reactor Netty基于Netty的底层封装设计到Spring WebFlux在Reactor Netty之上结合Spring Framework做的上层设计,以及其间的对接细节都做了很细致的分析和解读,希望读者可以对其中的很多代码设计进行举一反三,应用到自己的生产活动中。
在RouterFunctions中设定了nest、resources,它们也是我们在开发中经常会用到的东西,下面就对它们进行简单的介绍。
2. nest简介
由前面的内容可知,在函数式路由模式中,当对请求进行相应的条件判断并返回值为真时,会将该请求路由到对应的handlerFunction上。但我们也会遇到一个路径下还有子路径的情况,或者路径相同、请求方法不同等情况,为了应对这些情况,可以使用nest方法来创建嵌套路由,即可以由一组路由共享一个前缀路径、header或其他请求条件。
用下面的Demo来进行介绍。我们首先创建一个组合路由,访问同一个路径,可以通过GET方法来调用listUsers,得到用户列表,通过POST方法来调用createUser,创建一个新的user:
RouterFunction<ServerResponse> userRoutes =
RouterFunctions.route(RequestPredicates.method(HttpMethod.GET), this::listUsers)
.andRoute(RequestPredicates.method(HttpMethod.POST), this::createUser);
RouterFunction<ServerResponse> nestedRoute =
RouterFunctions.nest(RequestPredicates.path("/user"),userRoutes);
结合前面介绍的RouterFunctions,对接HttpHandler的自有实现,我们可以定义一个Spring WebFlux基于Netty的服务器:
public class Server {
public static final String HOST = "127.0.0.1";
public static final int PORT = 8081;
public static void main(String[] args) throws InterruptedException, IOException {
Server server = new Server();
server.startReactorServer();
System.out.println("Press ENTER to exit.");
System.in.read();
public void startReactorServer() throws InterruptedException {
RouterFunction<ServerResponse> route = routingFunction();
HttpHandler httpHandler = toHttpHandler(route);
ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
HttpServer.create()
.host(HOST)
.port(PORT)
.handle(adapter)
.bindNow()
.onDispose()
.block();
public RouterFunction<ServerResponse> routingFunction() {
UserRepository repository = new UserRepositorySample();
UserHandler handler = new UserHandler(repository);
return nest(path("/user"),
nest(accept(APPLICATION_JSON),
route(GET("/{id}"), handler::getAllUsers).andRoute(method(HttpMethod.GET),
handler::getAllUsers)).andRoute(POST("/").and(contentType(APPLICATION_JSON)),
handler::getAllUsers));
}
3. resources介绍
有时候我们需要在路由层面将一个请求路由到与给定根位置相关的资源。在这里,通过一个Demo来让大家看得更清楚一些:
Resource location = new FileSystemResource("public-resources/");
RoutingFunction<ServerResponse> resources = RouterFunctions.resources("/resources/**", location);
可以看到,当访问/resources/**时,直接到指定的位置"public-resources/"下找想要的资源。我们平时在开发中也可以通过经常接触的方式在WebConfig层面进行配置:
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override