Skip to content

Commit a05b748

Browse files
committed
InternalResourceViewResolver's exposure of context beans is now available at UrlBasedViewResolver level
Issue: SPR-8064
1 parent c06ac06 commit a05b748

File tree

4 files changed

+111
-100
lines changed

4 files changed

+111
-100
lines changed

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

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818

1919
import java.io.ByteArrayOutputStream;
2020
import java.io.IOException;
21+
import java.util.Arrays;
2122
import java.util.Collections;
23+
import java.util.HashSet;
2224
import java.util.LinkedHashMap;
2325
import java.util.Map;
2426
import java.util.Properties;
27+
import java.util.Set;
2528
import java.util.StringTokenizer;
2629
import javax.servlet.ServletOutputStream;
2730
import javax.servlet.http.HttpServletRequest;
@@ -30,6 +33,7 @@
3033
import org.springframework.beans.factory.BeanNameAware;
3134
import org.springframework.http.MediaType;
3235
import org.springframework.util.CollectionUtils;
36+
import org.springframework.web.context.support.ContextExposingHttpServletRequest;
3337
import org.springframework.web.context.support.WebApplicationObjectSupport;
3438
import org.springframework.web.servlet.View;
3539
import org.springframework.web.servlet.support.RequestContext;
@@ -68,12 +72,14 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
6872

6973
private String requestContextAttribute;
7074

71-
/** Map of static attributes, keyed by attribute name (String) */
7275
private final Map<String, Object> staticAttributes = new LinkedHashMap<String, Object>();
7376

74-
/** Whether or not the view should add path variables in the model */
7577
private boolean exposePathVariables = true;
7678

79+
private boolean exposeContextBeansAsAttributes = false;
80+
81+
private Set<String> exposedContextBeanNames;
82+
7783

7884
/**
7985
* Set the view's name. Helpful for traceability.
@@ -248,6 +254,36 @@ public boolean isExposePathVariables() {
248254
return this.exposePathVariables;
249255
}
250256

257+
/**
258+
* Set whether to make all Spring beans in the application context accessible
259+
* as request attributes, through lazy checking once an attribute gets accessed.
260+
* <p>This will make all such beans accessible in plain {@code ${...}}
261+
* expressions in a JSP 2.0 page, as well as in JSTL's {@code c:out}
262+
* value expressions.
263+
* <p>Default is "false". Switch this flag on to transparently expose all
264+
* Spring beans in the request attribute namespace.
265+
* <p><b>NOTE:</b> Context beans will override any custom request or session
266+
* attributes of the same name that have been manually added. However, model
267+
* attributes (as explicitly exposed to this view) of the same name will
268+
* always override context beans.
269+
* @see #getRequestToExpose
270+
*/
271+
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
272+
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
273+
}
274+
275+
/**
276+
* Specify the names of beans in the context which are supposed to be exposed.
277+
* If this is non-null, only the specified beans are eligible for exposure as
278+
* attributes.
279+
* <p>If you'd like to expose all Spring beans in the application context, switch
280+
* the {@link #setExposeContextBeansAsAttributes "exposeContextBeansAsAttributes"}
281+
* flag on but do not list specific bean names for this property.
282+
*/
283+
public void setExposedContextBeanNames(String... exposedContextBeanNames) {
284+
this.exposedContextBeanNames = new HashSet<String>(Arrays.asList(exposedContextBeanNames));
285+
}
286+
251287

252288
/**
253289
* Prepares the view given the specified model, merging it with static
@@ -264,7 +300,7 @@ public void render(Map<String, ?> model, HttpServletRequest request, HttpServlet
264300

265301
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
266302
prepareResponse(request, response);
267-
renderMergedOutputModel(mergedModel, request, response);
303+
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
268304
}
269305

270306
/**
@@ -276,12 +312,13 @@ protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, Http
276312

277313
@SuppressWarnings("unchecked")
278314
Map<String, Object> pathVars = (this.exposePathVariables ?
279-
(Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);
315+
(Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);
280316

281317
// Consolidate static and dynamic model attributes.
282318
int size = this.staticAttributes.size();
283-
size += (model != null) ? model.size() : 0;
284-
size += (pathVars != null) ? pathVars.size() : 0;
319+
size += (model != null ? model.size() : 0);
320+
size += (pathVars != null ? pathVars.size() : 0);
321+
285322
Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size);
286323
mergedModel.putAll(this.staticAttributes);
287324
if (pathVars != null) {
@@ -344,6 +381,24 @@ protected boolean generatesDownloadContent() {
344381
return false;
345382
}
346383

384+
/**
385+
* Get the request handle to expose to {@link #renderMergedOutputModel}, i.e. to the view.
386+
* <p>The default implementation wraps the original request for exposure of Spring beans
387+
* as request attributes (if demanded).
388+
* @param originalRequest the original servlet request as provided by the engine
389+
* @return the wrapped request, or the original request if no wrapping is necessary
390+
* @see #setExposeContextBeansAsAttributes
391+
* @see #setExposedContextBeanNames
392+
* @see org.springframework.web.context.support.ContextExposingHttpServletRequest
393+
*/
394+
protected HttpServletRequest getRequestToExpose(HttpServletRequest originalRequest) {
395+
if (this.exposeContextBeansAsAttributes || this.exposedContextBeanNames != null) {
396+
return new ContextExposingHttpServletRequest(
397+
originalRequest, getWebApplicationContext(), this.exposedContextBeanNames);
398+
}
399+
return originalRequest;
400+
}
401+
347402
/**
348403
* Subclasses must implement this method to actually render the view.
349404
* <p>The first step will be preparing the request: In the JSP case,

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

Lines changed: 7 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ public class InternalResourceView extends AbstractUrlBasedView {
6868

6969
private boolean alwaysInclude = false;
7070

71-
private boolean exposeContextBeansAsAttributes = false;
72-
73-
private Set<String> exposedContextBeanNames;
74-
7571
private boolean preventDispatchLoop = false;
7672

7773

@@ -115,36 +111,6 @@ public void setAlwaysInclude(boolean alwaysInclude) {
115111
this.alwaysInclude = alwaysInclude;
116112
}
117113

118-
/**
119-
* Set whether to make all Spring beans in the application context accessible
120-
* as request attributes, through lazy checking once an attribute gets accessed.
121-
* <p>This will make all such beans accessible in plain {@code ${...}}
122-
* expressions in a JSP 2.0 page, as well as in JSTL's {@code c:out}
123-
* value expressions.
124-
* <p>Default is "false". Switch this flag on to transparently expose all
125-
* Spring beans in the request attribute namespace.
126-
* <p><b>NOTE:</b> Context beans will override any custom request or session
127-
* attributes of the same name that have been manually added. However, model
128-
* attributes (as explicitly exposed to this view) of the same name will
129-
* always override context beans.
130-
* @see #getRequestToExpose
131-
*/
132-
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
133-
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
134-
}
135-
136-
/**
137-
* Specify the names of beans in the context which are supposed to be exposed.
138-
* If this is non-null, only the specified beans are eligible for exposure as
139-
* attributes.
140-
* <p>If you'd like to expose all Spring beans in the application context, switch
141-
* the {@link #setExposeContextBeansAsAttributes "exposeContextBeansAsAttributes"}
142-
* flag on but do not list specific bean names for this property.
143-
*/
144-
public void setExposedContextBeanNames(String... exposedContextBeanNames) {
145-
this.exposedContextBeanNames = new HashSet<String>(Arrays.asList(exposedContextBeanNames));
146-
}
147-
148114
/**
149115
* Set whether to explicitly prevent dispatching back to the
150116
* current handler path.
@@ -173,58 +139,38 @@ protected boolean isContextRequired() {
173139
protected void renderMergedOutputModel(
174140
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
175141

176-
// Determine which request handle to expose to the RequestDispatcher.
177-
HttpServletRequest requestToExpose = getRequestToExpose(request);
178-
179142
// Expose the model object as request attributes.
180-
exposeModelAsRequestAttributes(model, requestToExpose);
143+
exposeModelAsRequestAttributes(model, request);
181144

182145
// Expose helpers as request attributes, if any.
183-
exposeHelpers(requestToExpose);
146+
exposeHelpers(request);
184147

185148
// Determine the path for the request dispatcher.
186-
String dispatcherPath = prepareForRendering(requestToExpose, response);
149+
String dispatcherPath = prepareForRendering(request, response);
187150

188151
// Obtain a RequestDispatcher for the target resource (typically a JSP).
189-
RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
152+
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
190153
if (rd == null) {
191154
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
192155
"]: Check that the corresponding file exists within your web application archive!");
193156
}
194157

195158
// If already included or response already committed, perform include, else forward.
196-
if (useInclude(requestToExpose, response)) {
159+
if (useInclude(request, response)) {
197160
response.setContentType(getContentType());
198161
if (logger.isDebugEnabled()) {
199162
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
200163
}
201-
rd.include(requestToExpose, response);
164+
rd.include(request, response);
202165
}
203166

204167
else {
205168
// Note: The forwarded resource is supposed to determine the content type itself.
206169
if (logger.isDebugEnabled()) {
207170
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
208171
}
209-
rd.forward(requestToExpose, response);
210-
}
211-
}
212-
213-
/**
214-
* Get the request handle to expose to the RequestDispatcher, i.e. to the view.
215-
* <p>The default implementation wraps the original request for exposure of
216-
* Spring beans as request attributes (if demanded).
217-
* @param originalRequest the original servlet request as provided by the engine
218-
* @return the wrapped request, or the original request if no wrapping is necessary
219-
* @see #setExposeContextBeansAsAttributes
220-
* @see org.springframework.web.context.support.ContextExposingHttpServletRequest
221-
*/
222-
protected HttpServletRequest getRequestToExpose(HttpServletRequest originalRequest) {
223-
if (this.exposeContextBeansAsAttributes || this.exposedContextBeanNames != null) {
224-
return new ContextExposingHttpServletRequest(
225-
originalRequest, getWebApplicationContext(), this.exposedContextBeanNames);
172+
rd.forward(request, response);
226173
}
227-
return originalRequest;
228174
}
229175

230176
/**

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

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,6 @@ public class InternalResourceViewResolver extends UrlBasedViewResolver {
5252

5353
private Boolean alwaysInclude;
5454

55-
private Boolean exposeContextBeansAsAttributes;
56-
57-
private String[] exposedContextBeanNames;
58-
5955

6056
/**
6157
* Sets the default {@link #setViewClass view class} to {@link #requiredViewClass}:
@@ -89,42 +85,13 @@ public void setAlwaysInclude(boolean alwaysInclude) {
8985
this.alwaysInclude = alwaysInclude;
9086
}
9187

92-
/**
93-
* Set whether to make all Spring beans in the application context accessible
94-
* as request attributes, through lazy checking once an attribute gets accessed.
95-
* <p>This will make all such beans accessible in plain {@code ${...}}
96-
* expressions in a JSP 2.0 page, as well as in JSTL's {@code c:out}
97-
* value expressions.
98-
* <p>Default is "false".
99-
* @see InternalResourceView#setExposeContextBeansAsAttributes
100-
*/
101-
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
102-
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
103-
}
104-
105-
/**
106-
* Specify the names of beans in the context which are supposed to be exposed.
107-
* If this is non-null, only the specified beans are eligible for exposure as
108-
* attributes.
109-
* @see InternalResourceView#setExposedContextBeanNames
110-
*/
111-
public void setExposedContextBeanNames(String... exposedContextBeanNames) {
112-
this.exposedContextBeanNames = exposedContextBeanNames;
113-
}
114-
11588

11689
@Override
11790
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
11891
InternalResourceView view = (InternalResourceView) super.buildView(viewName);
11992
if (this.alwaysInclude != null) {
12093
view.setAlwaysInclude(this.alwaysInclude);
12194
}
122-
if (this.exposeContextBeansAsAttributes != null) {
123-
view.setExposeContextBeansAsAttributes(this.exposeContextBeansAsAttributes);
124-
}
125-
if (this.exposedContextBeanNames != null) {
126-
view.setExposedContextBeanNames(this.exposedContextBeanNames);
127-
}
12895
view.setPreventDispatchLoop(true);
12996
return view;
13097
}

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ public class UrlBasedViewResolver extends AbstractCachingViewResolver implements
119119

120120
private Boolean exposePathVariables;
121121

122+
private Boolean exposeContextBeansAsAttributes;
123+
124+
private String[] exposedContextBeanNames;
125+
122126
private String[] viewNames;
123127

124128
private int order = Integer.MAX_VALUE;
@@ -327,6 +331,37 @@ protected Boolean getExposePathVariables() {
327331
return this.exposePathVariables;
328332
}
329333

334+
/**
335+
* Set whether to make all Spring beans in the application context accessible
336+
* as request attributes, through lazy checking once an attribute gets accessed.
337+
* <p>This will make all such beans accessible in plain {@code ${...}}
338+
* expressions in a JSP 2.0 page, as well as in JSTL's {@code c:out}
339+
* value expressions.
340+
* <p>Default is "false".
341+
* @see AbstractView#setExposeContextBeansAsAttributes
342+
*/
343+
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
344+
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
345+
}
346+
347+
protected Boolean getExposeContextBeansAsAttributes() {
348+
return this.exposeContextBeansAsAttributes;
349+
}
350+
351+
/**
352+
* Specify the names of beans in the context which are supposed to be exposed.
353+
* If this is non-null, only the specified beans are eligible for exposure as
354+
* attributes.
355+
* @see AbstractView#setExposedContextBeanNames
356+
*/
357+
public void setExposedContextBeanNames(String... exposedContextBeanNames) {
358+
this.exposedContextBeanNames = exposedContextBeanNames;
359+
}
360+
361+
protected String[] getExposedContextBeanNames() {
362+
return this.exposedContextBeanNames;
363+
}
364+
330365
/**
331366
* Set the view names (or name patterns) that can be handled by this
332367
* {@link org.springframework.web.servlet.ViewResolver}. View names can contain
@@ -482,6 +517,14 @@ protected AbstractUrlBasedView buildView(String viewName) throws Exception {
482517
if (exposePathVariables != null) {
483518
view.setExposePathVariables(exposePathVariables);
484519
}
520+
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
521+
if (exposeContextBeansAsAttributes != null) {
522+
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
523+
}
524+
String[] exposedContextBeanNames = getExposedContextBeanNames();
525+
if (exposedContextBeanNames != null) {
526+
view.setExposedContextBeanNames(exposedContextBeanNames);
527+
}
485528

486529
return view;
487530
}

0 commit comments

Comments
 (0)