Spring3.2 MVC 分析

一世执手 提交于 2019-11-30 08:14:47

Spring3.2 MVC 分析:

SpringMVC现在应该用得很广泛了,其配置清晰,灵活度,定制能力等都是很强的,相比Struts2也是胜过一筹,还是从源码来分析一下,SpringMVC为我们做了什么。

  • 先从配置文件开始,看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等)进行渲染,然后返回到用户浏览器:

那到底要做些什么工作,才能达到上面这样的流程呢,往下说。

看看DispatcherServlet怎么来的:

先从HttpServletBean类入手,人类都知道Servlet被Servlet容器创建时,会调用一次其init方法,我们就看看HttpServletBean的init方法,其主要工作就是将Servlet配置参数映射到我们的Servlet Bean里面,然后再初始化子类(initServletBean):

@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是个空方法,留给子类去实现:

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上下文对象的:

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方法:

@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中:

# 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根上下文的建立过程。

收工。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!