Spring3.2 MVC 分析

Spring3.2 MVC 分析:

SpringMVC如今應該用得很普遍了,其配置清晰,靈活度,定製能力等都是很強的,相比Struts2也是賽過一籌,仍是從源碼來分析一下,SpringMVC爲咱們作了什麼。 java

  • 先從配置文件開始,看web.xml,用過SpringMVC的同窗應該都很熟悉:
<web-app>
    <listener>
	<listener-class>
	    org.springframework.web.context.ContextLoaderListener<!-- Spring根容器在這裏被創建起來 -->
	</listener-class>
    </listener>
    <context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath*:configs/beans.xml</param-value>
    </context-param>
    <!-- SpringMVC子容器會在DispatherServlet初始化過程當中被創建起來 -->
    <servlet>
        <servlet-name>example</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>example</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

</web-app>

看看SpringMVC官方的請求處理流程圖,DispacherServlet就是那個FrontController, 根據用戶請求的url,匹配到咱們自定義的控制器,而後進行業務處理,處理完成,將數據model返回給FrontController,而後它將數據+視圖模版(如jsp, freemarker等)進行渲染,而後返回到用戶瀏覽器: web

那到底要作些什麼工做,才能達到上面這樣的流程呢,往下說。 spring

看看DispatcherServlet怎麼來的: 瀏覽器

先從HttpServletBean類入手,人類都知道Servlet被Servlet容器建立時,會調用一次其init方法,咱們就看看HttpServletBean的init方法,其主要工做就是將Servlet配置參數映射到咱們的Servlet Bean裏面,而後再初始化子類(initServletBean): mvc

@Override
public final void init() throws ServletException {
    // Set bean properties from init parameters.
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
    ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
    bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
    initBeanWrapper(bw);
    bw.setPropertyValues(pvs, true);

    // Let subclasses do whatever initialization they like.
    initServletBean();
    ...
}
接着看initServletBean方法,該方法由FrameworkServlet實現, 從其名稱就知道,它是Spring Web框架的基礎Servlet, Spring這樣描述它的工做:
* Manages a WebApplicationContext instance per servlet. The servlet's configuration is determined by beans      in the servlet's namespace.
* Publishes events on request processing, whether or not a request is successfully handled.

它如何實現initServletBean, 它作的事很單一,就是初始化WebApplicationContext對象,initFrameworkServlet是個空方法,留給子類去實現: app

protected final void initServletBean() throws ServletException {
    ...		
    this.webApplicationContext = initWebApplicationContext();
    initFrameworkServlet();
    ...		
}

看看FrameworkServlet怎麼初始化WebApplicationContext的,其實相似於初始化IoC容器(如以前Spring Ioc文章講XmlClasspathApplicationContext): 框架

protected WebApplicationContext initWebApplicationContext() {                                                                   // 經過ServletContext對象獲取到ContextLoaderListener創建的根容器,它到底怎麼被創建起來的,後面再講
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    if (this.webApplicationContext != null) { //若經過構造參數傳遞了webApplicationContext
        // A context instance was injected at construction time -> use it
        wac = this.webApplicationContext;
        if (wac instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;                           if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent -> set
                    // the root application context (if any; may be null) as the parent
                    cwac.setParent(rootContext);
                }
                configureAndRefreshWebApplicationContext(cwac);
            }
        }
    }
    if (wac == null) {
        // 若是配置了contextAttribute屬性,經過其來獲取WebApplicationContext對象
        wac = findWebApplicationContext();
    }
    if (wac == null) { 
        // 這裏建立FrameworkServlet的上下文實例,默認實現是XmlWebApplicationContext, 且以根上下文爲父上下文
        wac = createWebApplicationContext(rootContext);
    }
    //觸發refresh事件, 會調用DispatcherServlet的onRefresh方法
    if (!this.refreshEventReceived) {
    }
    //默認會將FrameworkServlet對象的上下文對象存到ServletContext中
    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
	String attrName = getServletContextAttributeName();
	getServletContext().setAttribute(attrName, wac);
    }
    return wac;
}

咱們能夠看看createWebApplicationContext()怎麼創建其XmlWebApplicationContext上下文對象的: jsp

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
    Class<?> contextClass = getContextClass(); //沒有配置,則默認XmlWebApplicationContext.class
    ConfigurableWebApplicationContext wac =
	(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
    wac.setEnvironment(getEnvironment());
    wac.setParent(parent); //將根上下文做爲父上下文
    wac.setConfigLocation(getContextConfigLocation());//是否再xml中配置了contextConfigLocation屬性
    configureAndRefreshWebApplicationContext(wac); //如同以前文章將的Spring IoC就是初始化Ioc容器
    return wac;
}
那XmlWebApplicationContext的初始化與普通容器如XmlClassPathApplicationContext有什麼不一樣呢?其實現是很簡潔的:
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
	public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
	public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
	public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";

	@Override //這個方法就是再AbstractApplicationContext中的模版方法refresh中調用,和通常IoC容器初始化同樣
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}

	protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
	}

	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}

	@Override
	protected String[] getDefaultConfigLocations() {
		if (getNamespace() != null) { 
			return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};//就是web.xml中配置的DispatcherServlet的servlet-name,對應咱們就會有/WEB-INF/example-servlet.xml
		}
		else {
			return new String[] {DEFAULT_CONFIG_LOCATION};
		}
	}

}

簡單就分析完FrameworkServlet的上下文對象的構建過程,繼續看DispatcherServlet怎麼構建本身的,就從FrameworkServlet中initWebApplicationContext的onRefresh中,對應DispatcherServlet中的OnRefresh方法: async

@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context); //初始化multipartResolver bean, 用於文件上傳處理
    initLocaleResolver(context);    //初始化localeResolver bean, 用於解析用戶端Locale信息,默認實現AcceptHeaderLocaleResolver
    initThemeResolver(context); //初始化themeResolver bean, 默認實現FixedThemeResolver
    initHandlerMappings(context); //初始化handlerMappings集,將用戶請求映射到咱們定義的Controller中
    initHandlerAdapters(context); //初始化handlerAdapters
    initHandlerExceptionResolvers(context); //初始化異常處理解析器
    initRequestToViewNameTranslator(context); //初始化請求到視圖的翻譯器
    initViewResolvers(context); //初始化視圖解析器,如jsp, freemarker等
    initFlashMapManager(context); //初始化FlashMap管理器,實現再不一樣請求中共享參數,如重定向。
}

上面這些組件都有默認實現,spring將默認實現放在和DispatcherServlet.java同包下的DispatcherServlet.properties中: ide

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter

org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
重點看看handlerMappdings怎麼創建起來的:
private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
    //默認會從父上下文遞歸地去獲取HandlerMapping bean
    if (this.detectAllHandlerMappings) {
        // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                // We keep HandlerMappings in sorted order.
                OrderComparator.sort(this.handlerMappings);
            }
    }else { //僅從當前上下文獲取HandlerMapping bean
        HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
        this.handlerMappings = Collections.singletonList(hm);		
    }
    if (this.handlerMappings == null) { //若沒有配置,根據上面的屬性文件提供默認實現
        this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    }
}

看看HandlerMapping接口的定義,十分簡潔,就一個getHandler接口:

public interface HandlerMapping {
   ...
   //根據當前request對象,獲取HandlerExecutionChain對象,其包括一個handler對象(咱們定義的Controller)和一些定義的攔截器
   HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
其繼承樹:

HandlerExecutionChain確實保存了handler和interceptors:

public class HandlerExecutionChain {

    private final Object handler;

    private HandlerInterceptor[] interceptors;

    private List<HandlerInterceptor> interceptorList;

    private int interceptorIndex = -1;
    ...
}

看一個HandlerMapping的簡單實現SimpleUrlHandlerMapping, 咱們須要配置urlMap屬性,經過registerHandlers方法將urlMap的屬性註冊到父類的handlerMap中,其保存了url請求與handler bean的對應關係:

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
    private final Map<String, Object> urlMap = new HashMap<String, Object>();

    protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
        if (urlMap.isEmpty()) {
            logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
        } else {
            for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
            String url = entry.getKey();
            Object handler = entry.getValue();
            // Prepend with slash if not already present.
            if (!url.startsWith("/")) {
                url = "/" + url;
            }
            // Remove whitespace from handler bean name.
            if (handler instanceof String) {
                handler = ((String) handler).trim();
            }
            registerHandler(url, handler); //由父類實現
            }                                                                                                    }
}
繼續看AbstractUrlHandlerMapping怎麼實現registerHandler()方法:
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
    Object resolvedHandler = handler;
    // Eagerly resolve handler if referencing singleton via name.
    if (!this.lazyInitHandlers && handler instanceof String) {//若爲非懶加載且是單例bean,則從容器中獲取
        String handlerName = (String) handler;
        if (getApplicationContext().isSingleton(handlerName)) {
            resolvedHandler = getApplicationContext().getBean(handlerName);
        }
    }

    Object mappedHandler = this.handlerMap.get(urlPath);
    if (mappedHandler != null) { //一個url只能有一個對應的handler
        if (mappedHandler != resolvedHandler) {
            throw new IllegalStateException(
                "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
        }
    } else {
        if (urlPath.equals("/")) {
            setRootHandler(resolvedHandler);
        } else if (urlPath.equals("/*")) {
            setDefaultHandler(resolvedHandler);
        } else {
            this.handlerMap.put(urlPath, resolvedHandler);
        }
    }
}
這上面就是HandlerMapping bean怎麼被註冊起來的,下面是HandlerMapping Bean怎麼完成請求的映射處理的。

看看AbstractHandlerMapping的getHandler(), 根據當前請求來獲取對應的HandlerExecuteChain:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request); // 獲取handler對象
    if (handler == null) {
        handler = getDefaultHandler(); //默認Handler對象,就是上面註冊的"/"的handler對象
    }
    if (handler == null) {
        return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) { //若是是bean name就從容器中獲取
        String handlerName = (String) handler;
        handler = getApplicationContext().getBean(handlerName);
    }
    return getHandlerExecutionChain(handler, request); //獲取handlerExecutionChain
}
接着看getHandlerInternal怎麼得到handler對象(AbstractUrlHanlderMapping):
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); //提取請求url
    Object handler = lookupHandler(lookupPath, request); //根據url查找handler
    if (handler == null) {
        // We need to care for the default handler directly, since we need to
        // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
        Object rawHandler = null;
        if ("/".equals(lookupPath)) {
            rawHandler = getRootHandler();
        }
        if (rawHandler == null) {
            rawHandler = getDefaultHandler();
        }
        if (rawHandler != null) {
            // Bean name or resolved handler?
            if (rawHandler instanceof String) {
                String handlerName = (String) rawHandler;
                rawHandler = getApplicationContext().getBean(handlerName);
            }
            validateHandler(rawHandler, request);
            handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
        }
    }
    return handler;
}

由lookupHandler(url,request)查找handler對象:

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
    // Direct match?
    Object handler = this.handlerMap.get(urlPath); //精確匹配獲取handler
    if (handler != null) {
        // Bean name or resolved handler?
        if (handler instanceof String) { 
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }
        validateHandler(handler, request); //驗證handler,由子類實現
        return buildPathExposingHandler(handler, urlPath, urlPath, null);
    }
    //正則匹配獲取handler
    List<String> matchingPatterns = new ArrayList<String>();
    for (String registeredPattern : this.handlerMap.keySet()) {
        if (getPathMatcher().match(registeredPattern, urlPath)) {
            matchingPatterns.add(registeredPattern);
        }
    }
    String bestPatternMatch = null;
    Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
    if (!matchingPatterns.isEmpty()) {
        Collections.sort(matchingPatterns, patternComparator);
        bestPatternMatch = matchingPatterns.get(0);
    }
    if (bestPatternMatch != null) {
        handler = this.handlerMap.get(bestPatternMatch);
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }
        validateHandler(handler, request);
        String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);

        // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
        // for all of them
        Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
        for (String matchingPattern : matchingPatterns) {
            if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
                Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
                Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
                uriTemplateVariables.putAll(decodedVars);
            }
        }
        return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
    }
    // No handler found...
    return null;
}
上面就明白了根據請求獲取handler對象的過程, 下面講講請求是怎麼被DispatcherServlet分發處理的.

DispatcherServlet分發處理主要方法爲doServlce方法開始:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 若是request請求是include請求,則對請求屬性做一個快照
    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
       attributesSnapshot = new HashMap<String, Object>();
       Enumeration<?> attrNames = request.getAttributeNames();
       while (attrNames.hasMoreElements()) {
          String attrName = (String) attrNames.nextElement();
          if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
              attributesSnapshot.put(attrName, request.getAttribute(attrName));
          }
       }
    }

    //設置一些涉及MVC框架的屬性,以方便handler或view等引用.
    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
    //更新FlashMap屬性,該屬性是基於Session實現,主要是在不一樣request請求間傳遞參數,典型的就是Redirect這種狀況
    FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
    if (inputFlashMap != null) {
        request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
    }
    request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
    request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

    try {
        doDispatch(request, response); //真正實現將請求進行轉發
    } finally {
        if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
            return;
        }
        // Restore the original attribute snapshot, in case of an include.
        if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
        }
    }
}

看doDispatch():

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    ModelAndView mv = null;
    Exception dispatchException = null;
    processedRequest = checkMultipart(request);//檢查request是否Multipart, 便是否上傳文件
    multipartRequestParsed = processedRequest != request;

    //獲取當前請求對應的HandlerExecutionChain對象
    mappedHandler = getHandler(processedRequest, false);
    if (mappedHandler == null || mappedHandler.getHandler() == null) {
         noHandlerFound(processedRequest, response);
         return;
    }
    // Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    // Process last-modified header, if supported by the handler.
    String method = request.getMethod();
    boolean isGet = "GET".equals(method);
    if (isGet || "HEAD".equals(method)) {
       long lastModified = ha.getLastModified(request, mappedHandler.getHandler());			
       if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
          return;
       }
    }
    if (!mappedHandler.applyPreHandle(processedRequest, response)) {//前置處理,調用chain的攔截器預處理方法preHandle
          return;
    }
    ...
    //調用handler處理方法,最終會調用Controller的handleRequest方法,並返回ModelAndView對象
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    ...
    applyDefaultViewName(request, mv);//解析視圖名稱,就是咱們配置的prefix + uri + suffix
    mappedHandler.applyPostHandle(processedRequest, response, mv);//後置處理,調用chain的攔截器postHandle方法
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); //進行視圖渲染
    ...
}

接着看processDispatchResult()方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
    boolean errorView = false;
    if (exception != null) { //若是有異常
        if (exception instanceof ModelAndViewDefiningException) {
            logger.debug("ModelAndViewDefiningException encountered", exception);
            mv = ((ModelAndViewDefiningException) exception).getModelAndView();
        } else {
            Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
            mv = processHandlerException(request, response, handler, exception);
            errorView = (mv != null);
        }
    }

    // Did the handler return a view to render?
    if (mv != null && !mv.wasCleared()) {
        render(mv, request, response); //渲染視圖的最後一個步驟
	 if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }else {
        if (logger.isDebugEnabled()) {
	    logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
		    "': assuming HandlerAdapter completed request handling");
        }
    }

    if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
        // Concurrent handling started during a forward
        return;
    }

    if (mappedHandler != null) {
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}
看看render怎麼工做的:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // Determine locale for request and apply it to the response.
    Locale locale = this.localeResolver.resolveLocale(request);
    response.setLocale(locale);

    View view;
    if (mv.isReference()) { // 若ModalAndView中保存了view的引用
        //根據咱們配置的ViewReslover bean,解析視圖名稱
        view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
        if (view == null) {
            throw new ServletException(
                "Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" +
								getServletName() + "'");
        }
    } else {  //ModalAndView中保存了實際的view對象
        view = mv.getView();
        if (view == null) {
	    throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
						"View object in servlet with name '" + getServletName() + "'");
        }
    }
    view.render(mv.getModelInternal(), request, response); //渲染視圖
}
上面講述了DispatcherServlet的請求分發過程及視圖渲染大體流程。接下來得說說視圖渲染得話題,默認DispatcherServlet的默認視圖實是 InternalResourceViewResolver, 也就是jsp視圖,看看jsp視圖是怎麼被呈現的。

先看AbstractView的render方法:

public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);//合併動態和靜態屬性
    prepareResponse(request, response);
    renderMergedOutputModel(mergedModel, request, response); //用模型渲染視圖
}

看看InternalResourceView的renderMergeOutputModel怎麼實現的:

protected void renderMergedOutputModel(
			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response){

    // Determine which request handle to expose to the RequestDispatcher.
    HttpServletRequest requestToExpose = getRequestToExpose(request);

    //將model放入到request屬性中
    exposeModelAsRequestAttributes(model, requestToExpose);

    // Expose helpers as request attributes, if any.
    exposeHelpers(requestToExpose);

    //獲取request對象dispath的path
    String dispatcherPath = prepareForRendering(requestToExpose, response);

    //獲取RequestDispatcher對象
    RequestDispatcher rd = getRequestDispatcher(requestToExpose, 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)) {
        response.setContentType(getContentType());
        rd.include(requestToExpose, response);
    }else {
        // Note: The forwarded resource is supposed to determine the content type itself.
        exposeForwardRequestAttributes(requestToExpose);
        rd.forward(requestToExpose, response);
    }
}

這上面就講述了MVC中視圖的工做。

  • 關於Spring根容器的創建, 以前說了,Spring會創建根上下文,再創建MVC上下文,咱們簡略說下根上下文的創建,也就是web.xml的ContextLoaderListener配置:
<listener>
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>
看看ContextLoaderListener, 它繼承自ContextLoader, 主要初始化工做仍是由ContextLoader來完成
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
   ...
}
看看ContextLoader的initWebApplicationContext方法:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
	...
    }
    long startTime = System.currentTimeMillis();

    // Store context in local instance variable, to guarantee that
    // it is available on ServletContext shutdown.
    if (this.context == null) {
        this.context = createWebApplicationContext(servletContext);//建立根上下文
    }
    if (this.context instanceof ConfigurableWebApplicationContext) {
        ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
        if (!cwac.isActive()) {
            // The context has not yet been refreshed -> provide services such as
            // setting the parent context, setting the application context id, etc
            if (cwac.getParent() == null) {
            // The context instance was injected without an explicit parent ->
            // determine parent for root web application context, if any.
            ApplicationContext parent = loadParentContext(servletContext);
                cwac.setParent(parent);
            }
            configureAndRefreshWebApplicationContext(cwac, servletContext);//構建上下文,相似IoC容器構建
        }
    }                                                                                                           //將建立的根上下文保存到ServletContext, 因此再MVC上下文中能夠獲取到
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    ClassLoader ccl = Thread.currentThread().getContextClassLoader();
    if (ccl == ContextLoader.class.getClassLoader()) {
        currentContext = this.context;
    } else if (ccl != null) {
        currentContextPerThread.put(ccl, this.context);
    }
    return this.context;
}
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    Class<?> contextClass = determineContextClass(sc);//默認實現爲XmlWebApplicationContext
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
            "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }
    return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

這就簡略說了Spring根上下文的創建過程。

收工。

相關文章
相關標籤/搜索