From 7c4f0318bb32938105335099956eccd2807ce4fb Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 13 Mar 2020 16:08:34 +0100 Subject: [PATCH] Improve efficiency of built RouterFunction This commit changes the way RouterFunctions registered to the builder are composed in both WebFlux.fn and WebMvc.fn. Prior to this commit, all routes added to the build were composed with `reduce`. After this commit, all routes are stored in a special router function, allowing for more efficient execution and smaller stack traces. Closes gh-24652 --- .../server/RouterFunctionBuilder.java | 36 +++++++++++++++-- .../function/server/RouterFunctions.java | 2 +- .../function/RouterFunctionBuilder.java | 40 +++++++++++++++++-- .../web/servlet/function/RouterFunctions.java | 4 +- 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctionBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctionBuilder.java index 20e1c0a4be..aa751f490e 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctionBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctionBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.core.io.Resource; @@ -241,9 +242,10 @@ class RouterFunctionBuilder implements RouterFunctions.Builder { @Override public RouterFunction build() { - RouterFunction result = this.routerFunctions.stream() - .reduce(RouterFunction::and) - .orElseThrow(IllegalStateException::new); + if (this.routerFunctions.isEmpty()) { + throw new IllegalStateException("No routes registered. Register a route with GET(), POST(), etc."); + } + RouterFunction result = new BuiltRouterFunction(this.routerFunctions); if (this.filterFunctions.isEmpty()) { return result; @@ -258,4 +260,30 @@ class RouterFunctionBuilder implements RouterFunctions.Builder { } } + + /** + * Router function returned by {@link #build()} that simply iterates over the registered routes. + */ + private static class BuiltRouterFunction extends RouterFunctions.AbstractRouterFunction { + + private List> routerFunctions; + + public BuiltRouterFunction(List> routerFunctions) { + Assert.notEmpty(routerFunctions, "RouterFunctions must not be empty"); + this.routerFunctions = routerFunctions; + } + + @Override + public Mono> route(ServerRequest request) { + return Flux.fromIterable(this.routerFunctions) + .concatMap(routerFunction -> routerFunction.route(request)) + .next(); + } + + @Override + public void accept(RouterFunctions.Visitor visitor) { + this.routerFunctions.forEach(routerFunction -> routerFunction.accept(visitor)); + } + } + } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java index 85e6ee1e20..cd777c38db 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RouterFunctions.java @@ -749,7 +749,7 @@ public abstract class RouterFunctions { } - private abstract static class AbstractRouterFunction implements RouterFunction { + abstract static class AbstractRouterFunction implements RouterFunction { @Override public String toString() { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RouterFunctionBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RouterFunctionBuilder.java index 5eb17d9191..cc23d0144c 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RouterFunctionBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RouterFunctionBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -237,9 +237,10 @@ class RouterFunctionBuilder implements RouterFunctions.Builder { @Override public RouterFunction build() { - RouterFunction result = this.routerFunctions.stream() - .reduce(RouterFunction::and) - .orElseThrow(IllegalStateException::new); + if (this.routerFunctions.isEmpty()) { + throw new IllegalStateException("No routes registered. Register a route with GET(), POST(), etc."); + } + RouterFunction result = new BuiltRouterFunction(this.routerFunctions); if (this.filterFunctions.isEmpty()) { return result; @@ -254,4 +255,35 @@ class RouterFunctionBuilder implements RouterFunctions.Builder { } } + + /** + * Router function returned by {@link #build()} that simply iterates over the registered routes. + */ + private static class BuiltRouterFunction extends RouterFunctions.AbstractRouterFunction { + + private List> routerFunctions; + + public BuiltRouterFunction(List> routerFunctions) { + Assert.notEmpty(routerFunctions, "RouterFunctions must not be empty"); + this.routerFunctions = routerFunctions; + } + + @Override + public Optional> route(ServerRequest request) { + for (RouterFunction routerFunction : this.routerFunctions) { + Optional> result = routerFunction.route(request); + if (result.isPresent()) { + return result; + } + } + return Optional.empty(); + } + + @Override + public void accept(RouterFunctions.Visitor visitor) { + this.routerFunctions.forEach(routerFunction -> routerFunction.accept(visitor)); + } + } + + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RouterFunctions.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RouterFunctions.java index 92943358d3..9122b43629 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RouterFunctions.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/RouterFunctions.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -660,7 +660,7 @@ public abstract class RouterFunctions { } - private abstract static class AbstractRouterFunction implements RouterFunction { + abstract static class AbstractRouterFunction implements RouterFunction { @Override public String toString() {