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 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 {