SpringMVC源码阅读(二)

 

今天分析下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;
	}

}

 

JSTLView

package org.springframework.web.servlet.view;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.MessageSource;
import org.springframework.web.servlet.support.JstlUtils;
import org.springframework.web.servlet.support.RequestContext;

public class JstlView
  extends InternalResourceView
{
  private MessageSource messageSource;
  
  public JstlView() {}
  
  public JstlView(String url)
  {
    super(url);
  }
  
  public JstlView(String url, MessageSource messageSource)
  {
    this(url);
    this.messageSource = messageSource;
  }
  
  protected void initServletContext(ServletContext servletContext)
  {
    if (this.messageSource != null) {
      this.messageSource = JstlUtils.getJstlAwareMessageSource(servletContext, this.messageSource);
    }
    super.initServletContext(servletContext);
  }
  
  protected void exposeHelpers(HttpServletRequest request)
    throws Exception
  {
    if (this.messageSource != null) {
      JstlUtils.exposeLocalizationContext(request, this.messageSource);
    }
    else {
      JstlUtils.exposeLocalizationContext(new RequestContext(request, getServletContext()));
    }
  }
}

  

 

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;

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;
			}
		}
	}

	 
	protected Object getCacheKey(String viewName, Locale locale) {
		return viewName + "_" + locale;
	}

	 
	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();
		}
	}


	 
	protected View createView(String viewName, Locale locale) throws Exception {
		return loadView(viewName, locale);
	}

	protected abstract View loadView(String viewName, Locale locale) throws Exception;

}

 

package org.springframework.web.servlet;

import java.util.Locale;

public abstract interface ViewResolver
{
  public abstract View resolveViewName(String paramString, Locale paramLocale)
    throws Exception;
}

  

posted @ 2015-12-22 00:09  无心流泪  阅读(275)  评论(0编辑  收藏  举报