Skip to content

Commit d059241

Browse files
committed
Refactor FilterWebHandler and DefaultWebFilterChain
The chain is initialized once and re-used vs creating the "next" chains on every request.
1 parent 82a211f commit d059241

File tree

3 files changed

+71
-32
lines changed

3 files changed

+71
-32
lines changed
Lines changed: 63 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,11 +19,12 @@
1919
import java.util.Arrays;
2020
import java.util.Collections;
2121
import java.util.List;
22+
import java.util.ListIterator;
2223

2324
import reactor.core.publisher.Mono;
2425

26+
import org.springframework.lang.Nullable;
2527
import org.springframework.util.Assert;
26-
import org.springframework.util.ObjectUtils;
2728
import org.springframework.web.server.ServerWebExchange;
2829
import org.springframework.web.server.WebFilter;
2930
import org.springframework.web.server.WebFilterChain;
@@ -32,34 +33,80 @@
3233
/**
3334
* Default implementation of {@link WebFilterChain}.
3435
*
36+
* <p>Each instance of this class represents one link in the chain. The public
37+
* constructor {@link #DefaultWebFilterChain(WebHandler, List)}
38+
* initializes the full chain and represents its first link.
39+
*
40+
* <p>This class is immutable and thread-safe. It can be created once and
41+
* re-used to handle request concurrently.
42+
*
3543
* @author Rossen Stoyanchev
3644
* @since 5.0
3745
*/
3846
public class DefaultWebFilterChain implements WebFilterChain {
3947

40-
private final List<WebFilter> filters;
48+
private final List<WebFilter> allFilters;
4149

4250
private final WebHandler handler;
4351

44-
private final int index;
52+
@Nullable
53+
private final WebFilter currentFilter;
4554

55+
@Nullable
56+
private final DefaultWebFilterChain next;
4657

47-
public DefaultWebFilterChain(WebHandler handler, WebFilter... filters) {
58+
59+
/**
60+
* Public constructor with the list of filters and the target handler to use.
61+
* @param handler the target handler
62+
* @param filters the filters ahead of the handler
63+
* @since 5.1
64+
*/
65+
public DefaultWebFilterChain(WebHandler handler, List<WebFilter> filters) {
4866
Assert.notNull(handler, "WebHandler is required");
49-
this.filters = ObjectUtils.isEmpty(filters) ? Collections.emptyList() : Arrays.asList(filters);
67+
this.allFilters = Collections.unmodifiableList(filters);
5068
this.handler = handler;
51-
this.index = 0;
69+
DefaultWebFilterChain chain = initChain(filters, handler);
70+
this.currentFilter = chain.currentFilter;
71+
this.next = chain.next;
72+
}
73+
74+
private static DefaultWebFilterChain initChain(List<WebFilter> filters, WebHandler handler) {
75+
DefaultWebFilterChain chain = new DefaultWebFilterChain(filters, handler, null, null);
76+
ListIterator<? extends WebFilter> iterator = filters.listIterator(filters.size());
77+
while (iterator.hasPrevious()) {
78+
chain = new DefaultWebFilterChain(filters, handler, iterator.previous(), chain);
79+
}
80+
return chain;
5281
}
5382

54-
private DefaultWebFilterChain(DefaultWebFilterChain parent, int index) {
55-
this.filters = parent.getFilters();
56-
this.handler = parent.getHandler();
57-
this.index = index;
83+
/**
84+
* Private constructor to represent one link in the chain.
85+
*/
86+
private DefaultWebFilterChain(List<WebFilter> allFilters, WebHandler handler,
87+
@Nullable WebFilter currentFilter, @Nullable DefaultWebFilterChain next) {
88+
89+
this.allFilters = allFilters;
90+
this.currentFilter = currentFilter;
91+
this.handler = handler;
92+
this.next = next;
93+
}
94+
95+
/**
96+
* Public constructor with the list of filters and the target handler to use.
97+
* @param handler the target handler
98+
* @param filters the filters ahead of the handler
99+
* @deprecated as of 5.1 this constructor is deprecated in favor of
100+
* {@link #DefaultWebFilterChain(WebHandler, List)}.
101+
*/
102+
@Deprecated
103+
public DefaultWebFilterChain(WebHandler handler, WebFilter... filters) {
104+
this(handler, Arrays.asList(filters));
58105
}
59106

60107

61108
public List<WebFilter> getFilters() {
62-
return this.filters;
109+
return this.allFilters;
63110
}
64111

65112
public WebHandler getHandler() {
@@ -69,16 +116,10 @@ public WebHandler getHandler() {
69116

70117
@Override
71118
public Mono<Void> filter(ServerWebExchange exchange) {
72-
return Mono.defer(() -> {
73-
if (this.index < this.filters.size()) {
74-
WebFilter filter = this.filters.get(this.index);
75-
WebFilterChain chain = new DefaultWebFilterChain(this, this.index + 1);
76-
return filter.filter(exchange, chain);
77-
}
78-
else {
79-
return this.handler.handle(exchange);
80-
}
81-
});
119+
return Mono.defer(() ->
120+
this.currentFilter != null && this.next != null ?
121+
this.currentFilter.filter(exchange, this.next) :
122+
this.handler.handle(exchange));
82123
}
83124

84125
}

spring-web/src/main/java/org/springframework/web/server/handler/FilteringWebHandler.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.web.server.handler;
1818

19-
import java.util.Arrays;
2019
import java.util.List;
2120

2221
import reactor.core.publisher.Mono;
@@ -34,32 +33,30 @@
3433
*/
3534
public class FilteringWebHandler extends WebHandlerDecorator {
3635

37-
private final WebFilter[] filters;
36+
private final DefaultWebFilterChain chain;
3837

3938

4039
/**
4140
* Constructor.
4241
* @param filters the chain of filters
4342
*/
44-
public FilteringWebHandler(WebHandler webHandler, List<WebFilter> filters) {
45-
super(webHandler);
46-
this.filters = filters.toArray(new WebFilter[0]);
43+
public FilteringWebHandler(WebHandler handler, List<WebFilter> filters) {
44+
super(handler);
45+
this.chain = new DefaultWebFilterChain(handler, filters);
4746
}
4847

4948

5049
/**
5150
* Return a read-only list of the configured filters.
5251
*/
5352
public List<WebFilter> getFilters() {
54-
return Arrays.asList(this.filters);
53+
return this.chain.getFilters();
5554
}
5655

5756

5857
@Override
5958
public Mono<Void> handle(ServerWebExchange exchange) {
60-
return this.filters.length != 0 ?
61-
new DefaultWebFilterChain(getDelegate(), this.filters).filter(exchange) :
62-
super.handle(exchange);
59+
return this.chain.filter(exchange);
6360
}
6461

6562
}

spring-web/src/test/java/org/springframework/web/server/handler/FilteringWebHandlerTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2018 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,6 +42,7 @@
4242
import static org.junit.Assert.assertTrue;
4343

4444
/**
45+
* Unit tests for {@link FilteringWebHandler}.
4546
* @author Rossen Stoyanchev
4647
*/
4748
public class FilteringWebHandlerTests {

0 commit comments

Comments
 (0)