diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/AbstractMockServerSpec.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/AbstractMockServerSpec.java
index 8a1b5a0f89..5dd6cc0eeb 100644
--- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/AbstractMockServerSpec.java
+++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/AbstractMockServerSpec.java
@@ -18,7 +18,6 @@ package org.springframework.test.web.reactive.server;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import org.springframework.web.server.WebFilter;
@@ -61,7 +60,7 @@ abstract class AbstractMockServerSpec>
WebHttpHandlerBuilder builder = initHttpHandlerBuilder();
builder.filters(theFilters -> theFilters.addAll(0, this.filters));
this.configurers.forEach(configurer -> configurer.beforeServerCreated(builder));
- return new DefaultWebTestClientBuilder(builder.build());
+ return new DefaultWebTestClientBuilder(builder);
}
/**
diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java
index 1fe573acb4..479d6db7e9 100644
--- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java
+++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClient.java
@@ -71,15 +71,18 @@ class DefaultWebTestClient implements WebTestClient {
private final Duration timeout;
+ private final WebTestClient.Builder builder;
+
private final AtomicLong requestIndex = new AtomicLong();
DefaultWebTestClient(WebClient.Builder clientBuilder, ClientHttpConnector connector,
- @Nullable Duration timeout) {
+ @Nullable Duration timeout, WebTestClient.Builder webTestClientBuilder) {
Assert.notNull(clientBuilder, "WebClient.Builder is required");
this.wiretapConnector = new WiretapConnector(connector);
this.webClient = clientBuilder.clientConnector(this.wiretapConnector).build();
this.timeout = (timeout != null ? timeout : Duration.ofSeconds(5));
+ this.builder = webTestClientBuilder;
}
@@ -125,8 +128,7 @@ class DefaultWebTestClient implements WebTestClient {
@Override
public Builder mutate() {
- return new DefaultWebTestClientBuilder(this.wiretapConnector.getDelegate(),
- this.webClient.mutate(), this.timeout);
+ return this.builder;
}
private > UriSpec toUriSpec(
diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java
index b02194c862..5d07813ddf 100644
--- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java
+++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java
@@ -23,12 +23,13 @@ import java.util.function.Consumer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
-import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import org.springframework.web.util.UriBuilderFactory;
/**
@@ -41,29 +42,32 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
private final WebClient.Builder webClientBuilder;
+ private final WebHttpHandlerBuilder httpHandlerBuilder;
+
private final ClientHttpConnector connector;
private Duration responseTimeout;
DefaultWebTestClientBuilder() {
- this(new ReactorClientHttpConnector());
+ this(null, null, new ReactorClientHttpConnector(), null);
}
- DefaultWebTestClientBuilder(HttpHandler httpHandler) {
- this(new HttpHandlerConnector(httpHandler));
+ DefaultWebTestClientBuilder(WebHttpHandlerBuilder httpHandlerBuilder) {
+ this(null, httpHandlerBuilder, null, null);
}
- DefaultWebTestClientBuilder(ClientHttpConnector connector) {
- this(connector, null, null);
- }
-
- DefaultWebTestClientBuilder(ClientHttpConnector connector,
- @Nullable WebClient.Builder webClientBuilder,
+ DefaultWebTestClientBuilder(@Nullable WebClient.Builder webClientBuilder,
+ @Nullable WebHttpHandlerBuilder httpHandlerBuilder,
+ @Nullable ClientHttpConnector connector,
@Nullable Duration responseTimeout) {
- this.connector = connector;
+ Assert.isTrue(httpHandlerBuilder != null || connector !=null,
+ "Either WebHttpHandlerBuilder or ClientHttpConnector must be provided");
+
this.webClientBuilder = (webClientBuilder != null ? webClientBuilder : WebClient.builder());
+ this.httpHandlerBuilder = (httpHandlerBuilder != null ? httpHandlerBuilder.cloneBuilder() : null);
+ this.connector = connector;
this.responseTimeout = responseTimeout;
}
@@ -129,9 +133,24 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder {
return this;
}
+ @Override
+ public WebTestClient.Builder apply(WebTestClientConfigurer configurer) {
+ configurer.afterConfigurerAdded(this, this.httpHandlerBuilder, this.connector);
+ return this;
+ }
+
@Override
public WebTestClient build() {
- return new DefaultWebTestClient(this.webClientBuilder, this.connector, this.responseTimeout);
+
+ ClientHttpConnector connectorToUse = (this.connector != null ? this.connector :
+ new HttpHandlerConnector(this.httpHandlerBuilder.build()));
+
+ DefaultWebTestClientBuilder webTestClientBuilder = new DefaultWebTestClientBuilder(
+ this.webClientBuilder.build().mutate(), this.httpHandlerBuilder,
+ this.connector, this.responseTimeout);
+
+ return new DefaultWebTestClient(this.webClientBuilder,
+ connectorToUse, this.responseTimeout, webTestClientBuilder);
}
}
diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatorWebFilter.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatorWebFilter.java
deleted file mode 100644
index 7cb247394b..0000000000
--- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/ExchangeMutatorWebFilter.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2002-2017 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.test.web.reactive.server;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Function;
-
-import reactor.core.publisher.Mono;
-
-import org.springframework.http.HttpHeaders;
-import org.springframework.util.Assert;
-import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
-import org.springframework.web.server.ServerWebExchange;
-import org.springframework.web.server.WebFilter;
-import org.springframework.web.server.WebFilterChain;
-
-
-/**
- * Apply {@code ServerWebExchange} transformations during "mock" server tests
- * with the {@code WebTestClient}.
- *
- *
Register the {@code WebFilter} while setting up the mock server through
- * one of the following:
- *
- * - {@link WebTestClient#bindToController}
- *
- {@link WebTestClient#bindToRouterFunction}
- *
- {@link WebTestClient#bindToApplicationContext}
- *
- {@link WebTestClient#bindToWebHandler}
- *
- *
- * Example usage:
- *
- *
- * Function<ServerWebExchange, ServerWebExchange> fn1 = ...;
- * Function<ServerWebExchange, ServerWebExchange> fn2 = ...;
- *
- * ExchangeMutatorWebFilter mutator = new ExchangeMutatorWebFilter(fn1().andThen(fn2()));
- * WebTestClient client = WebTestClient.bindToController(new MyController()).webFilter(mutator).build();
- *
- *
- *
- * It is also possible to apply "per request" transformations:
- *
- *
- * ExchangeMutatorWebFilter mutator = new ExchangeMutatorWebFilter();
- * WebTestClient client = WebTestClient.bindToController(new MyController()).webFilter(mutator).build();
- *
- * Function<ServerWebExchange, ServerWebExchange> fn1 = ...;
- * Function<ServerWebExchange, ServerWebExchange> fn2 = ...;
- *
- * client.filter(mutator.perClient(fn1().andThen(fn2()))).get().uri("/").exchange();
- *
- *
- * @author Rossen Stoyanchev
- * @since 5.0
- */
-public class ExchangeMutatorWebFilter implements WebFilter {
-
- private final Function processor;
-
- private final Map> perRequestProcessors =
- new ConcurrentHashMap<>(4);
-
-
- public ExchangeMutatorWebFilter() {
- this(exchange -> exchange);
- }
-
- public ExchangeMutatorWebFilter(Function processor) {
- Assert.notNull(processor, "'processor' is required");
- this.processor = processor;
- }
-
-
- @Override
- public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
- exchange = getProcessor(exchange).apply(exchange);
- return chain.filter(exchange);
- }
-
- private Function getProcessor(ServerWebExchange exchange) {
- String id = getRequestId(exchange.getRequest().getHeaders());
- Function clientMutator = this.perRequestProcessors.remove(id);
- return (clientMutator != null ? this.processor.andThen(clientMutator) : this.processor);
- }
-
- private String getRequestId(HttpHeaders headers) {
- String id = headers.getFirst(WebTestClient.WEBTESTCLIENT_REQUEST_ID);
- Assert.notNull(id, "No \"" + WebTestClient.WEBTESTCLIENT_REQUEST_ID + "\" header");
- return id;
- }
-
- /**
- * Apply the given processor only to requests performed through the client
- * instance filtered with the returned filter. See class-level Javadoc for
- * sample code.
- * @param processor the exchange processor to use
- * @return client filter for use with {@link WebTestClient#filter}
- */
- public ExchangeFilterFunction perClient(Function processor) {
- return (request, next) -> {
- String id = getRequestId(request.headers());
- this.perRequestProcessors.compute(id,
- (s, value) -> value != null ? value.andThen(processor) : processor);
- return next.exchange(request);
- };
- }
-
-}
diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/MockServerConfigurer.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/MockServerConfigurer.java
index 31994f8cb4..c60c1e2148 100644
--- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/MockServerConfigurer.java
+++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/MockServerConfigurer.java
@@ -38,6 +38,7 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
*
* @author Rossen Stoyanchev
* @since 5.0
+ * @see WebTestClientConfigurer
*/
public interface MockServerConfigurer {
diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java
index 22f1d518c1..1e5165057d 100644
--- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java
+++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClient.java
@@ -193,13 +193,7 @@ public interface WebTestClient {
/**
* Register one or more {@link WebFilter} instances to apply to the
* mock server.
- *
- * This could be used for example to apply {@code ServerWebExchange}
- * transformations such as setting the Principal (for all requests or a
- * subset) via {@link ExchangeMutatorWebFilter}.
- *
* @param filter one or more filters
- * @see ExchangeMutatorWebFilter
*/
T webFilter(WebFilter... filter);
@@ -380,6 +374,12 @@ public interface WebTestClient {
*/
Builder responseTimeout(Duration timeout);
+ /**
+ *
+ * @param configurer
+ * @return
+ */
+ Builder apply(WebTestClientConfigurer configurer);
/**
* Build the {@link WebTestClient} instance.
diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClientConfigurer.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClientConfigurer.java
new file mode 100644
index 0000000000..0b390c0ba4
--- /dev/null
+++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/WebTestClientConfigurer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2017 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.test.web.reactive.server;
+
+import org.springframework.http.client.reactive.ClientHttpConnector;
+import org.springframework.lang.Nullable;
+import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
+
+/**
+ * Contract that frameworks or applications can use to pre-package a set of
+ * customizations to a {@link WebTestClient.Builder} and expose that
+ * as a shortcut.
+ *
+ * @author Rossen Stoyanchev
+ * @since 5.0
+ * @see MockServerConfigurer
+ */
+public interface WebTestClientConfigurer {
+
+ /**
+ * Invoked once only, immediately (i.e. before this method returns).
+ * @param builder the WebTestClient builder to make changes to
+ * @param httpHandlerBuilder the builder for the "mock server" HttpHandler
+ * this client was configured for "mock server" testing
+ * @param connector the connector for "live" integration tests if this
+ * server was configured for live integration testing
+ */
+ void afterConfigurerAdded(WebTestClient.Builder builder,
+ @Nullable WebHttpHandlerBuilder httpHandlerBuilder,
+ @Nullable ClientHttpConnector connector);
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ExchangeMutatorTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ExchangeMutatorTests.java
new file mode 100644
index 0000000000..12f310497f
--- /dev/null
+++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ExchangeMutatorTests.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2002-2017 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.test.web.reactive.server.samples;
+
+import java.security.Principal;
+
+import org.junit.Before;
+import org.junit.Test;
+import reactor.core.publisher.Mono;
+
+import org.springframework.http.client.reactive.ClientHttpConnector;
+import org.springframework.lang.Nullable;
+import org.springframework.test.web.reactive.server.MockServerConfigurer;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.test.web.reactive.server.WebTestClientConfigurer;
+import org.springframework.util.Assert;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
+
+/**
+ * Samples tests that demonstrate applying ServerWebExchange initialization.
+ * @author Rossen Stoyanchev
+ */
+public class ExchangeMutatorTests {
+
+ private WebTestClient webTestClient;
+
+
+ @Before
+ public void setUp() throws Exception {
+
+ this.webTestClient = WebTestClient.bindToController(new TestController())
+ .apply(globalIdentity("Pablo"))
+ .build();
+ }
+
+ @Test
+ public void useGloballyConfiguredIdentity() throws Exception {
+ this.webTestClient.get().uri("/userIdentity")
+ .exchange()
+ .expectStatus().isOk()
+ .expectBody(String.class).isEqualTo("Hello Pablo!");
+ }
+
+ @Test
+ public void useLocallyConfiguredIdentity() throws Exception {
+
+ withIdentity(this.webTestClient, "Giovanni")
+ .get().uri("/userIdentity")
+ .exchange()
+ .expectStatus().isOk()
+ .expectBody(String.class).isEqualTo("Hello Giovanni!");
+ }
+
+
+ private static MockServerConfigurer globalIdentity(String userName) {
+ return new IdentityConfigurer(userName);
+ }
+
+ private static WebTestClient withIdentity(WebTestClient client, String userName) {
+ return client.mutate().apply(new IdentityConfigurer(userName)).build();
+ }
+
+
+ @RestController
+ static class TestController {
+
+ @GetMapping("/userIdentity")
+ public String handle(Principal principal) {
+ return "Hello " + principal.getName() + "!";
+ }
+ }
+
+ private static class TestUser implements Principal {
+
+ private final String name;
+
+ TestUser(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+ }
+
+ private static class IdentityConfigurer implements MockServerConfigurer, WebTestClientConfigurer {
+
+ private final IdentityFilter filter;
+
+
+ public IdentityConfigurer(String userName) {
+ this.filter = new IdentityFilter(userName);
+ }
+
+ @Override
+ public void beforeServerCreated(WebHttpHandlerBuilder builder) {
+ builder.filters(filters -> filters.add(0, this.filter));
+ }
+
+ @Override
+ public void afterConfigurerAdded(WebTestClient.Builder builder,
+ @Nullable WebHttpHandlerBuilder httpHandlerBuilder,
+ @Nullable ClientHttpConnector connector) {
+
+ Assert.notNull(httpHandlerBuilder, "Not a mock server");
+ httpHandlerBuilder.filters(filters -> {
+ filters.removeIf(filter -> filter instanceof IdentityFilter);
+ filters.add(0, this.filter);
+ });
+ }
+ }
+
+ private static class IdentityFilter implements WebFilter {
+
+ private final Mono userMono;
+
+
+ IdentityFilter(String userName) {
+ this.userMono = Mono.just(new TestUser(userName));
+ }
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, WebFilterChain chain) {
+ exchange = exchange.mutate().principal(this.userMono).build();
+ return chain.filter(exchange);
+ }
+ }
+
+}
diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ExchangeMutatorWebFilterTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ExchangeMutatorWebFilterTests.java
deleted file mode 100644
index 1aeb38cbf9..0000000000
--- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/samples/ExchangeMutatorWebFilterTests.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2002-2017 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.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.springframework.test.web.reactive.server.samples;
-
-import java.security.Principal;
-import java.util.function.UnaryOperator;
-
-import org.junit.Before;
-import org.junit.Test;
-import reactor.core.publisher.Mono;
-
-import org.springframework.test.web.reactive.server.ExchangeMutatorWebFilter;
-import org.springframework.test.web.reactive.server.WebTestClient;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.server.ServerWebExchange;
-
-/**
- * Samples tests that demonstrate applying ServerWebExchange initialization.
- * @author Rossen Stoyanchev
- */
-public class ExchangeMutatorWebFilterTests {
-
- private ExchangeMutatorWebFilter exchangeMutator;
-
- private WebTestClient webTestClient;
-
-
- @Before
- public void setUp() throws Exception {
-
- this.exchangeMutator = new ExchangeMutatorWebFilter(userIdentity("Pablo"));
-
- this.webTestClient = WebTestClient.bindToController(new TestController())
- .webFilter(this.exchangeMutator)
- .build();
- }
-
- @Test
- public void globalMutator() throws Exception {
- this.webTestClient.get().uri("/userIdentity")
- .exchange()
- .expectStatus().isOk()
- .expectBody(String.class).isEqualTo("Hello Pablo!");
- }
-
- @Test
- public void perRequestMutators() throws Exception {
-
- this.webTestClient = WebTestClient.bindToController(new TestController())
- .webFilter(this.exchangeMutator)
- .configureClient()
- .filter(this.exchangeMutator.perClient(userIdentity("Giovanni")))
- .build();
-
- this.webTestClient
- .get().uri("/userIdentity")
- .exchange()
- .expectStatus().isOk()
- .expectBody(String.class).isEqualTo("Hello Giovanni!");
- }
-
-
- private UnaryOperator userIdentity(String userName) {
- return exchange -> exchange.mutate().principal(Mono.just(new TestUser(userName))).build();
- }
-
-
- @RestController
- static class TestController {
-
- @GetMapping("/userIdentity")
- public String handle(Principal principal) {
- return "Hello " + principal.getName() + "!";
- }
- }
-
-
- private static class TestUser implements Principal {
-
- private final String name;
-
- TestUser(String name) {
- this.name = name;
- }
-
- @Override
- public String getName() {
- return this.name;
- }
- }
-
-}
diff --git a/spring-web/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java b/spring-web/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java
index 5b1c680528..43217c8ddf 100644
--- a/spring-web/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java
+++ b/spring-web/src/main/java/org/springframework/web/server/adapter/WebHttpHandlerBuilder.java
@@ -96,6 +96,19 @@ public class WebHttpHandlerBuilder {
this.webHandler = webHandler;
}
+ /**
+ * Copy constructor.
+ */
+ private WebHttpHandlerBuilder(WebHttpHandlerBuilder other) {
+
+ this.webHandler = other.webHandler;
+ this.filters.addAll(other.filters);
+ this.exceptionHandlers.addAll(other.exceptionHandlers);
+ this.sessionManager = other.sessionManager;
+ this.codecConfigurer = other.codecConfigurer;
+ this.localeContextResolver = other.localeContextResolver;
+ }
+
/**
* Static factory method to create a new builder instance.
@@ -263,6 +276,14 @@ public class WebHttpHandlerBuilder {
return adapted;
}
+ /**
+ * Clone this {@link WebHttpHandlerBuilder}.
+ * @return the cloned builder instance
+ */
+ public WebHttpHandlerBuilder cloneBuilder() {
+ return new WebHttpHandlerBuilder(this);
+ }
+
private static class SortedBeanContainer {
diff --git a/spring-web/src/main/java/org/springframework/web/server/handler/ExceptionHandlingWebHandler.java b/spring-web/src/main/java/org/springframework/web/server/handler/ExceptionHandlingWebHandler.java
index 4d0e976a36..01dd84d5bb 100644
--- a/spring-web/src/main/java/org/springframework/web/server/handler/ExceptionHandlingWebHandler.java
+++ b/spring-web/src/main/java/org/springframework/web/server/handler/ExceptionHandlingWebHandler.java
@@ -16,6 +16,7 @@
package org.springframework.web.server.handler;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -40,7 +41,7 @@ public class ExceptionHandlingWebHandler extends WebHandlerDecorator {
public ExceptionHandlingWebHandler(WebHandler delegate, List handlers) {
super(delegate);
- this.exceptionHandlers = Collections.unmodifiableList(handlers);
+ this.exceptionHandlers = Collections.unmodifiableList(new ArrayList<>(handlers));
}