diff --git a/spring-web/src/main/java/org/springframework/web/server/handler/DefaultWebFilterChain.java b/spring-web/src/main/java/org/springframework/web/server/handler/DefaultWebFilterChain.java
index 38ce73d802..06729c3a58 100644
--- a/spring-web/src/main/java/org/springframework/web/server/handler/DefaultWebFilterChain.java
+++ b/spring-web/src/main/java/org/springframework/web/server/handler/DefaultWebFilterChain.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -19,11 +19,12 @@ package org.springframework.web.server.handler;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.ListIterator;
import reactor.core.publisher.Mono;
+import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
-import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
@@ -32,34 +33,80 @@ import org.springframework.web.server.WebHandler;
/**
* Default implementation of {@link WebFilterChain}.
*
+ *
Each instance of this class represents one link in the chain. The public
+ * constructor {@link #DefaultWebFilterChain(WebHandler, List)}
+ * initializes the full chain and represents its first link.
+ *
+ *
This class is immutable and thread-safe. It can be created once and
+ * re-used to handle request concurrently.
+ *
* @author Rossen Stoyanchev
* @since 5.0
*/
public class DefaultWebFilterChain implements WebFilterChain {
- private final List filters;
+ private final List allFilters;
private final WebHandler handler;
- private final int index;
+ @Nullable
+ private final WebFilter currentFilter;
+
+ @Nullable
+ private final DefaultWebFilterChain next;
- public DefaultWebFilterChain(WebHandler handler, WebFilter... filters) {
+ /**
+ * Public constructor with the list of filters and the target handler to use.
+ * @param handler the target handler
+ * @param filters the filters ahead of the handler
+ * @since 5.1
+ */
+ public DefaultWebFilterChain(WebHandler handler, List filters) {
Assert.notNull(handler, "WebHandler is required");
- this.filters = ObjectUtils.isEmpty(filters) ? Collections.emptyList() : Arrays.asList(filters);
+ this.allFilters = Collections.unmodifiableList(filters);
this.handler = handler;
- this.index = 0;
+ DefaultWebFilterChain chain = initChain(filters, handler);
+ this.currentFilter = chain.currentFilter;
+ this.next = chain.next;
}
- private DefaultWebFilterChain(DefaultWebFilterChain parent, int index) {
- this.filters = parent.getFilters();
- this.handler = parent.getHandler();
- this.index = index;
+ private static DefaultWebFilterChain initChain(List filters, WebHandler handler) {
+ DefaultWebFilterChain chain = new DefaultWebFilterChain(filters, handler, null, null);
+ ListIterator extends WebFilter> iterator = filters.listIterator(filters.size());
+ while (iterator.hasPrevious()) {
+ chain = new DefaultWebFilterChain(filters, handler, iterator.previous(), chain);
+ }
+ return chain;
+ }
+
+ /**
+ * Private constructor to represent one link in the chain.
+ */
+ private DefaultWebFilterChain(List allFilters, WebHandler handler,
+ @Nullable WebFilter currentFilter, @Nullable DefaultWebFilterChain next) {
+
+ this.allFilters = allFilters;
+ this.currentFilter = currentFilter;
+ this.handler = handler;
+ this.next = next;
+ }
+
+ /**
+ * Public constructor with the list of filters and the target handler to use.
+ * @param handler the target handler
+ * @param filters the filters ahead of the handler
+ * @deprecated as of 5.1 this constructor is deprecated in favor of
+ * {@link #DefaultWebFilterChain(WebHandler, List)}.
+ */
+ @Deprecated
+ public DefaultWebFilterChain(WebHandler handler, WebFilter... filters) {
+ this(handler, Arrays.asList(filters));
}
public List getFilters() {
- return this.filters;
+ return this.allFilters;
}
public WebHandler getHandler() {
@@ -69,16 +116,10 @@ public class DefaultWebFilterChain implements WebFilterChain {
@Override
public Mono filter(ServerWebExchange exchange) {
- return Mono.defer(() -> {
- if (this.index < this.filters.size()) {
- WebFilter filter = this.filters.get(this.index);
- WebFilterChain chain = new DefaultWebFilterChain(this, this.index + 1);
- return filter.filter(exchange, chain);
- }
- else {
- return this.handler.handle(exchange);
- }
- });
+ return Mono.defer(() ->
+ this.currentFilter != null && this.next != null ?
+ this.currentFilter.filter(exchange, this.next) :
+ this.handler.handle(exchange));
}
}
diff --git a/spring-web/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java b/spring-web/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java
index fc705bceca..ff3e6045d8 100644
--- a/spring-web/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java
+++ b/spring-web/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java
@@ -16,7 +16,6 @@
package org.springframework.web.server.handler;
-import java.util.Arrays;
import java.util.List;
import reactor.core.publisher.Mono;
@@ -34,16 +33,16 @@ import org.springframework.web.server.WebHandler;
*/
public class FilteringWebHandler extends WebHandlerDecorator {
- private final WebFilter[] filters;
+ private final DefaultWebFilterChain chain;
/**
* Constructor.
* @param filters the chain of filters
*/
- public FilteringWebHandler(WebHandler webHandler, List filters) {
- super(webHandler);
- this.filters = filters.toArray(new WebFilter[0]);
+ public FilteringWebHandler(WebHandler handler, List filters) {
+ super(handler);
+ this.chain = new DefaultWebFilterChain(handler, filters);
}
@@ -51,15 +50,13 @@ public class FilteringWebHandler extends WebHandlerDecorator {
* Return a read-only list of the configured filters.
*/
public List getFilters() {
- return Arrays.asList(this.filters);
+ return this.chain.getFilters();
}
@Override
public Mono handle(ServerWebExchange exchange) {
- return this.filters.length != 0 ?
- new DefaultWebFilterChain(getDelegate(), this.filters).filter(exchange) :
- super.handle(exchange);
+ return this.chain.filter(exchange);
}
}
diff --git a/spring-web/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java b/spring-web/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java
index f35710b2b2..48e8ae5b22 100644
--- a/spring-web/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java
+++ b/spring-web/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2018 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.
@@ -42,6 +42,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
+ * Unit tests for {@link FilteringWebHandler}.
* @author Rossen Stoyanchev
*/
public class FilteringWebHandlerTests {