Skip to content

Commit 59aee92

Browse files
fred84poutsma
authored andcommitted
Add filter to AbstractCachingViewResolver
This commit introduces a filter that specifies whether a View should be cached by the AbstractCachingViewResolver or not. Closes gh-22391
1 parent 4612544 commit 59aee92

File tree

3 files changed

+137
-1
lines changed

3 files changed

+137
-1
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/view/AbstractCachingViewResolver.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ public void render(@Nullable Map<String, ?> model, HttpServletRequest request, H
6868
/** Fast access cache for Views, returning already cached instances without a global lock. */
6969
private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<>(DEFAULT_CACHE_LIMIT);
7070

71+
/** Filter function which determines if view should be cached. */
72+
private ViewCacheFilter viewCacheFilter = (viewName, view, locale) -> true;
73+
7174
/** Map from view key to View instance, synchronized for View creation. */
7275
@SuppressWarnings("serial")
7376
private final Map<Object, View> viewCreationCache =
@@ -134,6 +137,21 @@ public void setCacheUnresolved(boolean cacheUnresolved) {
134137
this.cacheUnresolved = cacheUnresolved;
135138
}
136139

140+
/**
141+
* Filter function which determines if view should be cached.
142+
* Default behaviour is to cache all views.
143+
*/
144+
public void setViewCacheFilter(ViewCacheFilter cacheFilter) {
145+
this.viewCacheFilter = cacheFilter;
146+
}
147+
148+
/**
149+
* Return filter function which determines if view should be cached.
150+
*/
151+
public ViewCacheFilter getViewCacheFilter() {
152+
return this.viewCacheFilter;
153+
}
154+
137155
/**
138156
* Return if caching of unresolved views is enabled.
139157
*/
@@ -160,7 +178,7 @@ public View resolveViewName(String viewName, Locale locale) throws Exception {
160178
if (view == null && this.cacheUnresolved) {
161179
view = UNRESOLVED_VIEW;
162180
}
163-
if (view != null) {
181+
if (view != null && this.viewCacheFilter.filter(viewName, view, locale)) {
164182
this.viewAccessCache.put(cacheKey, view);
165183
this.viewCreationCache.put(cacheKey, view);
166184
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.servlet.view;
18+
19+
import java.util.Locale;
20+
21+
import org.springframework.web.servlet.View;
22+
23+
/**
24+
* Filter which determines if view should be cached in {@link AbstractCachingViewResolver}.
25+
*
26+
* @author Sergey Galkin
27+
*/
28+
@FunctionalInterface
29+
public interface ViewCacheFilter {
30+
31+
boolean filter(String viewName, View view, Locale locale);
32+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.servlet.view;
18+
19+
import static org.mockito.ArgumentMatchers.any;
20+
import static org.mockito.ArgumentMatchers.eq;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.times;
23+
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.when;
25+
26+
import java.util.Locale;
27+
import org.junit.Test;
28+
import org.springframework.web.servlet.View;
29+
30+
public class ViewResolverCacheFilterTest {
31+
32+
private interface ViewLoader {
33+
View load(String viewName);
34+
}
35+
36+
private static class TestViewResolver extends AbstractCachingViewResolver {
37+
38+
private final ViewLoader viewLoader;
39+
40+
private TestViewResolver(ViewLoader viewLoader) {
41+
this.viewLoader = viewLoader;
42+
}
43+
44+
@Override
45+
protected View loadView(String viewName, Locale locale) {
46+
return viewLoader.load(viewName);
47+
}
48+
}
49+
50+
private final static String VIEW_NAME = "name";
51+
private final ViewLoader viewLoader = mock(ViewLoader.class);
52+
private final TestViewResolver resolver = new TestViewResolver(viewLoader);
53+
54+
@Test
55+
public void viewWillBePlacedInCache() throws Exception {
56+
resolver.setViewCacheFilter((n, v, l) -> true);
57+
58+
resolver.resolveViewName(VIEW_NAME, Locale.ENGLISH);
59+
resolver.resolveViewName(VIEW_NAME, Locale.ENGLISH);
60+
61+
verify(viewLoader, times(1)).load(any());
62+
}
63+
64+
@Test
65+
public void viewWillNotBePlacedInCached() throws Exception {
66+
resolver.setViewCacheFilter((n, v, l) -> false);
67+
68+
resolver.resolveViewName(VIEW_NAME, Locale.ENGLISH);
69+
resolver.resolveViewName(VIEW_NAME, Locale.ENGLISH);
70+
71+
verify(viewLoader, times(2)).load(any());
72+
}
73+
74+
@Test
75+
public void verifyPassedParamsToFilter() throws Exception {
76+
View view = mock(View.class);
77+
when(viewLoader.load(any())).thenReturn(view);
78+
79+
ViewCacheFilter filter = mock(ViewCacheFilter.class);
80+
resolver.setViewCacheFilter(filter);
81+
82+
resolver.resolveViewName(VIEW_NAME, Locale.ENGLISH);
83+
84+
verify(filter, times(1)).filter(eq(VIEW_NAME), eq(view), eq(Locale.ENGLISH));
85+
}
86+
}

0 commit comments

Comments
 (0)