InternalResourceViewResolver's exposure of context beans is now available at UrlBasedViewResolver level
Issue: SPR-8064
This commit is contained in:
@@ -18,10 +18,13 @@ package org.springframework.web.servlet.view;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -30,6 +33,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.context.support.ContextExposingHttpServletRequest;
|
||||
import org.springframework.web.context.support.WebApplicationObjectSupport;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.support.RequestContext;
|
||||
@@ -68,12 +72,14 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
|
||||
|
||||
private String requestContextAttribute;
|
||||
|
||||
/** Map of static attributes, keyed by attribute name (String) */
|
||||
private final Map<String, Object> staticAttributes = new LinkedHashMap<String, Object>();
|
||||
|
||||
/** Whether or not the view should add path variables in the model */
|
||||
private boolean exposePathVariables = true;
|
||||
|
||||
private boolean exposeContextBeansAsAttributes = false;
|
||||
|
||||
private Set<String> exposedContextBeanNames;
|
||||
|
||||
|
||||
/**
|
||||
* Set the view's name. Helpful for traceability.
|
||||
@@ -248,6 +254,36 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
|
||||
return this.exposePathVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to make all Spring beans in the application context accessible
|
||||
* as request attributes, through lazy checking once an attribute gets accessed.
|
||||
* <p>This will make all such beans accessible in plain {@code ${...}}
|
||||
* expressions in a JSP 2.0 page, as well as in JSTL's {@code c:out}
|
||||
* value expressions.
|
||||
* <p>Default is "false". Switch this flag on to transparently expose all
|
||||
* Spring beans in the request attribute namespace.
|
||||
* <p><b>NOTE:</b> Context beans will override any custom request or session
|
||||
* attributes of the same name that have been manually added. However, model
|
||||
* attributes (as explicitly exposed to this view) of the same name will
|
||||
* always override context beans.
|
||||
* @see #getRequestToExpose
|
||||
*/
|
||||
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
|
||||
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the names of beans in the context which are supposed to be exposed.
|
||||
* If this is non-null, only the specified beans are eligible for exposure as
|
||||
* attributes.
|
||||
* <p>If you'd like to expose all Spring beans in the application context, switch
|
||||
* the {@link #setExposeContextBeansAsAttributes "exposeContextBeansAsAttributes"}
|
||||
* flag on but do not list specific bean names for this property.
|
||||
*/
|
||||
public void setExposedContextBeanNames(String... exposedContextBeanNames) {
|
||||
this.exposedContextBeanNames = new HashSet<String>(Arrays.asList(exposedContextBeanNames));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepares the view given the specified model, merging it with static
|
||||
@@ -264,7 +300,7 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
|
||||
|
||||
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
|
||||
prepareResponse(request, response);
|
||||
renderMergedOutputModel(mergedModel, request, response);
|
||||
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -276,12 +312,13 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> pathVars = (this.exposePathVariables ?
|
||||
(Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);
|
||||
(Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null);
|
||||
|
||||
// Consolidate static and dynamic model attributes.
|
||||
int size = this.staticAttributes.size();
|
||||
size += (model != null) ? model.size() : 0;
|
||||
size += (pathVars != null) ? pathVars.size() : 0;
|
||||
size += (model != null ? model.size() : 0);
|
||||
size += (pathVars != null ? pathVars.size() : 0);
|
||||
|
||||
Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size);
|
||||
mergedModel.putAll(this.staticAttributes);
|
||||
if (pathVars != null) {
|
||||
@@ -344,6 +381,24 @@ public abstract class AbstractView extends WebApplicationObjectSupport implement
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request handle to expose to {@link #renderMergedOutputModel}, i.e. to the view.
|
||||
* <p>The default implementation wraps the original request for exposure of Spring beans
|
||||
* as request attributes (if demanded).
|
||||
* @param originalRequest the original servlet request as provided by the engine
|
||||
* @return the wrapped request, or the original request if no wrapping is necessary
|
||||
* @see #setExposeContextBeansAsAttributes
|
||||
* @see #setExposedContextBeanNames
|
||||
* @see org.springframework.web.context.support.ContextExposingHttpServletRequest
|
||||
*/
|
||||
protected HttpServletRequest getRequestToExpose(HttpServletRequest originalRequest) {
|
||||
if (this.exposeContextBeansAsAttributes || this.exposedContextBeanNames != null) {
|
||||
return new ContextExposingHttpServletRequest(
|
||||
originalRequest, getWebApplicationContext(), this.exposedContextBeanNames);
|
||||
}
|
||||
return originalRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses must implement this method to actually render the view.
|
||||
* <p>The first step will be preparing the request: In the JSP case,
|
||||
|
||||
@@ -68,10 +68,6 @@ public class InternalResourceView extends AbstractUrlBasedView {
|
||||
|
||||
private boolean alwaysInclude = false;
|
||||
|
||||
private boolean exposeContextBeansAsAttributes = false;
|
||||
|
||||
private Set<String> exposedContextBeanNames;
|
||||
|
||||
private boolean preventDispatchLoop = false;
|
||||
|
||||
|
||||
@@ -115,36 +111,6 @@ public class InternalResourceView extends AbstractUrlBasedView {
|
||||
this.alwaysInclude = alwaysInclude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to make all Spring beans in the application context accessible
|
||||
* as request attributes, through lazy checking once an attribute gets accessed.
|
||||
* <p>This will make all such beans accessible in plain {@code ${...}}
|
||||
* expressions in a JSP 2.0 page, as well as in JSTL's {@code c:out}
|
||||
* value expressions.
|
||||
* <p>Default is "false". Switch this flag on to transparently expose all
|
||||
* Spring beans in the request attribute namespace.
|
||||
* <p><b>NOTE:</b> Context beans will override any custom request or session
|
||||
* attributes of the same name that have been manually added. However, model
|
||||
* attributes (as explicitly exposed to this view) of the same name will
|
||||
* always override context beans.
|
||||
* @see #getRequestToExpose
|
||||
*/
|
||||
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
|
||||
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the names of beans in the context which are supposed to be exposed.
|
||||
* If this is non-null, only the specified beans are eligible for exposure as
|
||||
* attributes.
|
||||
* <p>If you'd like to expose all Spring beans in the application context, switch
|
||||
* the {@link #setExposeContextBeansAsAttributes "exposeContextBeansAsAttributes"}
|
||||
* flag on but do not list specific bean names for this property.
|
||||
*/
|
||||
public void setExposedContextBeanNames(String... exposedContextBeanNames) {
|
||||
this.exposedContextBeanNames = new HashSet<String>(Arrays.asList(exposedContextBeanNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to explicitly prevent dispatching back to the
|
||||
* current handler path.
|
||||
@@ -173,32 +139,29 @@ public class InternalResourceView extends AbstractUrlBasedView {
|
||||
protected void renderMergedOutputModel(
|
||||
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
// Determine which request handle to expose to the RequestDispatcher.
|
||||
HttpServletRequest requestToExpose = getRequestToExpose(request);
|
||||
|
||||
// Expose the model object as request attributes.
|
||||
exposeModelAsRequestAttributes(model, requestToExpose);
|
||||
exposeModelAsRequestAttributes(model, request);
|
||||
|
||||
// Expose helpers as request attributes, if any.
|
||||
exposeHelpers(requestToExpose);
|
||||
exposeHelpers(request);
|
||||
|
||||
// Determine the path for the request dispatcher.
|
||||
String dispatcherPath = prepareForRendering(requestToExpose, response);
|
||||
String dispatcherPath = prepareForRendering(request, response);
|
||||
|
||||
// Obtain a RequestDispatcher for the target resource (typically a JSP).
|
||||
RequestDispatcher rd = getRequestDispatcher(requestToExpose, dispatcherPath);
|
||||
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
|
||||
if (rd == null) {
|
||||
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
|
||||
"]: Check that the corresponding file exists within your web application archive!");
|
||||
}
|
||||
|
||||
// If already included or response already committed, perform include, else forward.
|
||||
if (useInclude(requestToExpose, response)) {
|
||||
if (useInclude(request, response)) {
|
||||
response.setContentType(getContentType());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
|
||||
}
|
||||
rd.include(requestToExpose, response);
|
||||
rd.include(request, response);
|
||||
}
|
||||
|
||||
else {
|
||||
@@ -206,27 +169,10 @@ public class InternalResourceView extends AbstractUrlBasedView {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
|
||||
}
|
||||
rd.forward(requestToExpose, response);
|
||||
rd.forward(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request handle to expose to the RequestDispatcher, i.e. to the view.
|
||||
* <p>The default implementation wraps the original request for exposure of
|
||||
* Spring beans as request attributes (if demanded).
|
||||
* @param originalRequest the original servlet request as provided by the engine
|
||||
* @return the wrapped request, or the original request if no wrapping is necessary
|
||||
* @see #setExposeContextBeansAsAttributes
|
||||
* @see org.springframework.web.context.support.ContextExposingHttpServletRequest
|
||||
*/
|
||||
protected HttpServletRequest getRequestToExpose(HttpServletRequest originalRequest) {
|
||||
if (this.exposeContextBeansAsAttributes || this.exposedContextBeanNames != null) {
|
||||
return new ContextExposingHttpServletRequest(
|
||||
originalRequest, getWebApplicationContext(), this.exposedContextBeanNames);
|
||||
}
|
||||
return originalRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose helpers unique to each rendering operation. This is necessary so that
|
||||
* different rendering operations can't overwrite each other's contexts etc.
|
||||
|
||||
@@ -52,10 +52,6 @@ public class InternalResourceViewResolver extends UrlBasedViewResolver {
|
||||
|
||||
private Boolean alwaysInclude;
|
||||
|
||||
private Boolean exposeContextBeansAsAttributes;
|
||||
|
||||
private String[] exposedContextBeanNames;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the default {@link #setViewClass view class} to {@link #requiredViewClass}:
|
||||
@@ -89,29 +85,6 @@ public class InternalResourceViewResolver extends UrlBasedViewResolver {
|
||||
this.alwaysInclude = alwaysInclude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to make all Spring beans in the application context accessible
|
||||
* as request attributes, through lazy checking once an attribute gets accessed.
|
||||
* <p>This will make all such beans accessible in plain {@code ${...}}
|
||||
* expressions in a JSP 2.0 page, as well as in JSTL's {@code c:out}
|
||||
* value expressions.
|
||||
* <p>Default is "false".
|
||||
* @see InternalResourceView#setExposeContextBeansAsAttributes
|
||||
*/
|
||||
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
|
||||
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the names of beans in the context which are supposed to be exposed.
|
||||
* If this is non-null, only the specified beans are eligible for exposure as
|
||||
* attributes.
|
||||
* @see InternalResourceView#setExposedContextBeanNames
|
||||
*/
|
||||
public void setExposedContextBeanNames(String... exposedContextBeanNames) {
|
||||
this.exposedContextBeanNames = exposedContextBeanNames;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
|
||||
@@ -119,12 +92,6 @@ public class InternalResourceViewResolver extends UrlBasedViewResolver {
|
||||
if (this.alwaysInclude != null) {
|
||||
view.setAlwaysInclude(this.alwaysInclude);
|
||||
}
|
||||
if (this.exposeContextBeansAsAttributes != null) {
|
||||
view.setExposeContextBeansAsAttributes(this.exposeContextBeansAsAttributes);
|
||||
}
|
||||
if (this.exposedContextBeanNames != null) {
|
||||
view.setExposedContextBeanNames(this.exposedContextBeanNames);
|
||||
}
|
||||
view.setPreventDispatchLoop(true);
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,10 @@ public class UrlBasedViewResolver extends AbstractCachingViewResolver implements
|
||||
|
||||
private Boolean exposePathVariables;
|
||||
|
||||
private Boolean exposeContextBeansAsAttributes;
|
||||
|
||||
private String[] exposedContextBeanNames;
|
||||
|
||||
private String[] viewNames;
|
||||
|
||||
private int order = Integer.MAX_VALUE;
|
||||
@@ -327,6 +331,37 @@ public class UrlBasedViewResolver extends AbstractCachingViewResolver implements
|
||||
return this.exposePathVariables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to make all Spring beans in the application context accessible
|
||||
* as request attributes, through lazy checking once an attribute gets accessed.
|
||||
* <p>This will make all such beans accessible in plain {@code ${...}}
|
||||
* expressions in a JSP 2.0 page, as well as in JSTL's {@code c:out}
|
||||
* value expressions.
|
||||
* <p>Default is "false".
|
||||
* @see AbstractView#setExposeContextBeansAsAttributes
|
||||
*/
|
||||
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) {
|
||||
this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes;
|
||||
}
|
||||
|
||||
protected Boolean getExposeContextBeansAsAttributes() {
|
||||
return this.exposeContextBeansAsAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the names of beans in the context which are supposed to be exposed.
|
||||
* If this is non-null, only the specified beans are eligible for exposure as
|
||||
* attributes.
|
||||
* @see AbstractView#setExposedContextBeanNames
|
||||
*/
|
||||
public void setExposedContextBeanNames(String... exposedContextBeanNames) {
|
||||
this.exposedContextBeanNames = exposedContextBeanNames;
|
||||
}
|
||||
|
||||
protected String[] getExposedContextBeanNames() {
|
||||
return this.exposedContextBeanNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the view names (or name patterns) that can be handled by this
|
||||
* {@link org.springframework.web.servlet.ViewResolver}. View names can contain
|
||||
@@ -482,6 +517,14 @@ public class UrlBasedViewResolver extends AbstractCachingViewResolver implements
|
||||
if (exposePathVariables != null) {
|
||||
view.setExposePathVariables(exposePathVariables);
|
||||
}
|
||||
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
|
||||
if (exposeContextBeansAsAttributes != null) {
|
||||
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
|
||||
}
|
||||
String[] exposedContextBeanNames = getExposedContextBeanNames();
|
||||
if (exposedContextBeanNames != null) {
|
||||
view.setExposedContextBeanNames(exposedContextBeanNames);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user