标签:
今天分析下ViewResolver和View的实现 下面是ModelAndView的实现
package org.springframework.web.servlet;
import java.util.Map;
import org.springframework.ui.ModelMap;
import org.springframework.util.CollectionUtils;
public class ModelAndView {
/** View instance or view name String */
private Object view;
/** Model Map */
private ModelMap model;
/** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */
private boolean cleared = false;
/**
* Default constructor for bean-style usage: populating bean
* properties instead of passing in constructor arguments.
* @see #setView(View)
* @see #setViewName(String)
*/
public ModelAndView() {
}
public ModelAndView(String viewName) {
this.view = viewName;
}
public ModelAndView(View view) {
this.view = view;
}
public ModelAndView(String viewName, Map<String, ?> model) {
this.view = viewName;
if (model != null) {
getModelMap().addAllAttributes(model);
}
}
public ModelAndView(View view, Map<String, ?> model) {
this.view = view;
if (model != null) {
getModelMap().addAllAttributes(model);
}
}
public ModelAndView(String viewName, String modelName, Object modelObject) {
this.view = viewName;
addObject(modelName, modelObject);
}
public ModelAndView(View view, String modelName, Object modelObject) {
this.view = view;
addObject(modelName, modelObject);
}
public void setViewName(String viewName) {
this.view = viewName;
}
/**
* Return the view name to be resolved by the DispatcherServlet
* via a ViewResolver, or <code>null</code> if we are using a View object.
*/
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
/**
* Set a View object for this ModelAndView. Will override any
* pre-existing view name or View.
*/
public void setView(View view) {
this.view = view;
}
/**
* Return the View object, or <code>null</code> if we are using a view name
* to be resolved by the DispatcherServlet via a ViewResolver.
*/
public View getView() {
return (this.view instanceof View ? (View) this.view : null);
}
/**
* Indicate whether or not this <code>ModelAndView</code> has a view, either
* as a view name or as a direct {@link View} instance.
*/
public boolean hasView() {
return (this.view != null);
}
/**
* Return whether we use a view reference, i.e. <code>true</code>
* if the view has been specified via a name to be resolved by the
* DispatcherServlet via a ViewResolver.
*/
public boolean isReference() {
return (this.view instanceof String);
}
protected Map<String, Object> getModelInternal() {
return this.model;
}
public ModelMap getModelMap() {
if (this.model == null) {
this.model = new ModelMap();
}
return this.model;
}
public Map<String, Object> getModel() {
return getModelMap();
}
public ModelAndView addObject(String attributeName, Object attributeValue) {
getModelMap().addAttribute(attributeName, attributeValue);
return this;
}
public ModelAndView addObject(Object attributeValue) {
getModelMap().addAttribute(attributeValue);
return this;
}
public ModelAndView addAllObjects(Map<String, ?> modelMap) {
getModelMap().addAllAttributes(modelMap);
return this;
}
public void clear() {
this.view = null;
this.model = null;
this.cleared = true;
}
public boolean isEmpty() {
return (this.view == null && CollectionUtils.isEmpty(this.model));
}
public boolean wasCleared() {
return (this.cleared && isEmpty());
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ModelAndView: ");
if (isReference()) {
sb.append("reference to view with name ‘").append(this.view).append("‘");
}
else {
sb.append("materialized View is [").append(this.view).append(‘]‘);
}
sb.append("; model is ").append(this.model);
return sb.toString();
}
}
与逻辑视图紧紧相连的View
package org.springframework.web.servlet;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface View {
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
String getContentType();
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
ViewResolver的一个实现类
package org.springframework.web.servlet.view;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.BeanUtils;
import org.springframework.core.Ordered;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.servlet.View;
public class UrlBasedViewResolver extends AbstractCachingViewResolver implements Ordered {
public static final String REDIRECT_URL_PREFIX = "redirect:";
public static final String FORWARD_URL_PREFIX = "forward:";
private Class viewClass;
private String prefix = "";
private String suffix = "";
private String[] viewNames = null;
private String contentType;
private boolean redirectContextRelative = true;
private boolean redirectHttp10Compatible = true;
private String requestContextAttribute;
private int order = Integer.MAX_VALUE;
private final Map<String, Object> staticAttributes = new HashMap<String, Object>();
public void setViewClass(Class viewClass) {
if (viewClass == null || !requiredViewClass().isAssignableFrom(viewClass)) {
throw new IllegalArgumentException(
"Given view class [" + (viewClass != null ? viewClass.getName() : null) +
"] is not of type [" + requiredViewClass().getName() + "]");
}
this.viewClass = viewClass;
}
protected Class getViewClass() {
return this.viewClass;
}
protected Class requiredViewClass() {
return AbstractUrlBasedView.class;
}
public void setPrefix(String prefix) {
this.prefix = (prefix != null ? prefix : "");
}
protected String getPrefix() {
return this.prefix;
}
public void setSuffix(String suffix) {
this.suffix = (suffix != null ? suffix : "");
}
protected String getSuffix() {
return this.suffix;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
protected String getContentType() {
return this.contentType;
}
public void setRedirectContextRelative(boolean redirectContextRelative) {
this.redirectContextRelative = redirectContextRelative;
}
protected boolean isRedirectContextRelative() {
return this.redirectContextRelative;
}
public void setRedirectHttp10Compatible(boolean redirectHttp10Compatible) {
this.redirectHttp10Compatible = redirectHttp10Compatible;
}
protected boolean isRedirectHttp10Compatible() {
return this.redirectHttp10Compatible;
}
public void setRequestContextAttribute(String requestContextAttribute) {
this.requestContextAttribute = requestContextAttribute;
}
protected String getRequestContextAttribute() {
return this.requestContextAttribute;
}
public void setAttributes(Properties props) {
CollectionUtils.mergePropertiesIntoMap(props, this.staticAttributes);
}
public void setAttributesMap(Map<String, ?> attributes) {
if (attributes != null) {
this.staticAttributes.putAll(attributes);
}
}
public Map<String, Object> getAttributesMap() {
return this.staticAttributes;
}
public void setViewNames(String[] viewNames) {
this.viewNames = viewNames;
}
protected String[] getViewNames() {
return this.viewNames;
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
@Override
protected void initApplicationContext() {
super.initApplicationContext();
if (getViewClass() == null) {
throw new IllegalArgumentException("Property ‘viewClass‘ is required");
}
}
@Override
protected Object getCacheKey(String viewName, Locale locale) {
return viewName;
}
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
}
/**
protected boolean canHandle(String viewName, Locale locale) {
String[] viewNames = getViewNames();
return (viewNames == null || PatternMatchUtils.simpleMatch(viewNames, viewName));
}
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
View result = (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
return (view.checkResource(locale) ? result : null);
}
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
return view;
}
}
/*
* Copyright 2002-2009 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.view;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.springframework.web.context.support.WebApplicationObjectSupport;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
/**
* Convenient base class for {@link org.springframework.web.servlet.ViewResolver}
* implementations. Caches {@link org.springframework.web.servlet.View} objects
* once resolved: This means that view resolution won‘t be a performance problem,
* no matter how costly initial view retrieval is.
*
* <p>Subclasses need to implement the {@link #loadView} template method,
* building the View object for a specific view name and locale.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see #loadView
*/
public abstract class AbstractCachingViewResolver extends WebApplicationObjectSupport implements ViewResolver {
/** Whether we should cache views, once resolved */
private boolean cache = true;
/** Map from view key to View instance */
private final Map<Object, View> viewCache = new HashMap<Object, View>();
/**
* Enable or disable caching.
* <p>Default is "true": caching is enabled.
* Disable this only for debugging and development.
* <p><b>Warning: Disabling caching can severely impact performance.</b>
*/
public void setCache(boolean cache) {
this.cache = cache;
}
/**
* Return if caching is enabled.
*/
public boolean isCache() {
return this.cache;
}
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
synchronized (this.viewCache) {
View view = this.viewCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
return view;
}
}
}
/**
* Return the cache key for the given view name and the given locale.
* <p>Default is a String consisting of view name and locale suffix.
* Can be overridden in subclasses.
* <p>Needs to respect the locale in general, as a different locale can
* lead to a different view resource.
*/
protected Object getCacheKey(String viewName, Locale locale) {
return viewName + "_" + locale;
}
/**
* Provides functionality to clear the cache for a certain view.
* <p>This can be handy in case developer are able to modify views
* (e.g. Velocity templates) at runtime after which you‘d need to
* clear the cache for the specified view.
* @param viewName the view name for which the cached view object
* (if any) needs to be removed
* @param locale the locale for which the view object should be removed
*/
public void removeFromCache(String viewName, Locale locale) {
if (!this.cache) {
logger.warn("View caching is SWITCHED OFF -- removal not necessary");
}
else {
Object cacheKey = getCacheKey(viewName, locale);
Object cachedView;
synchronized (this.viewCache) {
cachedView = this.viewCache.remove(cacheKey);
}
if (cachedView == null) {
// Some debug output might be useful...
if (logger.isDebugEnabled()) {
logger.debug("No cached instance for view ‘" + cacheKey + "‘ was found");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Cache for view " + cacheKey + " has been cleared");
}
}
}
}
/**
* Clear the entire view cache, removing all cached view objects.
* Subsequent resolve calls will lead to recreation of demanded view objects.
*/
public void clearCache() {
logger.debug("Clearing entire view cache");
synchronized (this.viewCache) {
this.viewCache.clear();
}
}
/**
* Create the actual View object.
* <p>The default implementation delegates to {@link #loadView}.
* This can be overridden to resolve certain view names in a special fashion,
* before delegating to the actual <code>loadView</code> implementation
* provided by the subclass.
* @param viewName the name of the view to retrieve
* @param locale the Locale to retrieve the view for
* @return the View instance, or <code>null</code> if not found
* (optional, to allow for ViewResolver chaining)
* @throws Exception if the view couldn‘t be resolved
* @see #loadView
*/
protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale);
}
/**
* Subclasses must implement this method, building a View object
* for the specified view. The returned View objects will be
* cached by this ViewResolver base class.
* <p>Subclasses are not forced to support internationalization:
* A subclass that does not may simply ignore the locale parameter.
* @param viewName the name of the view to retrieve
* @param locale the Locale to retrieve the view for
* @return the View instance, or <code>null</code> if not found
* (optional, to allow for ViewResolver chaining)
* @throws Exception if the view couldn‘t be resolved
* @see #resolveViewName
*/
protected abstract View loadView(String viewName, Locale locale) throws Exception;
}
标签:
原文地址:http://www.cnblogs.com/wuxinliulei/p/5065345.html