Spring系列之MVC

2017-05-11  本文已影响0人  康康不遛猫

1、Spring MVC类图和流程

(1)、Spring MVC流程

Paste_Image.png

例子


Paste_Image.png

时序图


Paste_Image.png
流程描述
  1. 用户向服务器发送请求,请求被Spring 前端控制DispatcherServlet捕获;
  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
  3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
  4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
    HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
    数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
    数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
    数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
  5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
  6. 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
  7. ViewResolver 结合Model和View,来渲染视图
  8. 将渲染结果返回给客户端。

(2)、类图

Paste_Image.png

(3)、ContextLoaderListener初始化的上下文webapplicationcontext和DispatcherServlet之间的关系

Paste_Image.png

2、源码解析

(1)、ContextLoaderListener初始化过程

Paste_Image.png Paste_Image.png

ContextLoaderListener实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的contextInitialized方法。

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
            throw new IllegalStateException(
                    "Cannot initialize context because there is already a root application context present - " +
                    "check whether you have multiple ContextLoader* definitions in your web.xml!");
        }
 
        Log logger = LogFactory.getLog(ContextLoader.class);
        servletContext.log("Initializing Spring root WebApplicationContext");
        if (logger.isInfoEnabled()) {
            logger.info("Root WebApplicationContext: initialization started");
        }
        long startTime = System.currentTimeMillis();
 
        try {
            // Store context in local instance variable, to guarantee that
            // it is available on ServletContext shutdown.
            if (this.context == null) {
                //此时spring容器为null,此处获取Spring IOC容器对象
                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);
                        //此处设置的为null
                        cwac.setParent(parent);
                    }
                    //此处进行初始化容器操作
                    configureAndRefreshWebApplicationContext(cwac, servletContext);
                }
            }
            //设置servletContext的org.springframework.web.context.WebApplicationContext.ROOT属性

            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);
            }
 
            if (logger.isDebugEnabled()) {
                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
                        WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
            }
            if (logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
            }
 
            return this.context;
        }
        catch (RuntimeException ex) {
            logger.error("Context initialization failed", ex);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
            throw ex;
        }
        catch (Error err) {
            logger.error("Context initialization failed", err);
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
            throw err;
        }
 }

configureAndRefreshWebApplicationContext方法,调用AbstractApplicationContext的refresh方法初始化容器操作

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
            if (idParam != null) {
                wac.setId(idParam);
            }
            else {
                // Generate default id...
                if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
                    // Servlet <= 2.4: resort to name specified in web.xml, if any.
                    wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                            ObjectUtils.getDisplayString(sc.getServletContextName()));
                }
                else {
                    wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                            ObjectUtils.getDisplayString(sc.getContextPath()));
                }
            }
        }
 
        wac.setServletContext(sc);
        //获取contextConfigLocation下spring容器的配置文件
        String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
        if (initParameter != null) {
            wac.setConfigLocation(initParameter);
        }
        customizeContext(sc, wac);
        //此处调用AbstractApplicationContext的refresh方法,初始化容器
        wac.refresh();
 }

(2)、DispatcherServlet初始化过程

Paste_Image.png

首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;

其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;

再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。

最后,有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是XmlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的IOC上下文)定义的那些bean。

FrameworkServlet的initWebApplicationContext

protected WebApplicationContext initWebApplicationContext() {
        //获取rootContext ,即spring的IoC容器对象
       //(这个对象在ContextLoaderListener初始化IOC容器时就已经把它set到ServletContext的属性中)
        WebApplicationContext rootContext =
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        //Sping MVC容器WebApplicationContext 
        WebApplicationContext wac = null;
        //不走该流程
        if (this.webApplicationContext != null) {
            // 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) {
            // No context instance was injected at construction time -> see if one
            // has been registered in the servlet context. If one exists, it is assumed
            // that the parent context (if any) has already been set and that the
            // user has performed any initialization such as setting the context id
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            // No context instance is defined for this servlet -> create a local one
            //创建和初始化spring mvc容器,代码(2-1)
            wac = createWebApplicationContext(rootContext);
        }
 
        if (!this.refreshEventReceived) {
            // Either the context is not a ConfigurableApplicationContext with refresh
            // support or the context injected at construction time had already been
            // refreshed -> trigger initial onRefresh manually here.
            //调用DispatcherServlet的initStrategies方法,代码(2-2)
            onRefresh(wac);
        }
 
        if (this.publishContext) {
            // Publish the context as a servlet context attribute.
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
                        "' as ServletContext attribute with name [" + attrName + "]");
            }
        }
 
        return wac;
 } 

(2-1)、创建WebApplicationContext

createWebApplicationContext创建和初始化mvc容器,调用FrameworkServlet的configureAndRefreshWebApplicationContext

//createWebApplicationContext
protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
        return createWebApplicationContext((ApplicationContext) parent);
}

//createWebApplicationContext
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
        Class<?> contextClass = getContextClass();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Servlet with name '" + getServletName() +
                    "' will try to create custom WebApplicationContext context of class '" +
                    contextClass.getName() + "'" + ", using parent context [" + parent + "]");
        }
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException(
                    "Fatal initialization error in servlet with name '" + getServletName() +
                    "': custom WebApplicationContext class [" + contextClass.getName() +
                    "] is not of type ConfigurableWebApplicationContext");
        }
        //创建mvc容器对象
        ConfigurableWebApplicationContext wac =
                (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
 
        wac.setEnvironment(getEnvironment());
        wac.setParent(parent);
        wac.setConfigLocation(getContextConfigLocation());
 
        configureAndRefreshWebApplicationContext(wac);
 
        return wac;
 }

FrameworkServlet的configureAndRefreshWebApplicationContext

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            if (this.contextId != null) {
                wac.setId(this.contextId);
            }
            else {
                // Generate default id...
                ServletContext sc = getServletContext();
                if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
                    // Servlet <= 2.4: resort to name specified in web.xml, if any.
                    String servletContextName = sc.getServletContextName();
                    if (servletContextName != null) {
                        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + servletContextName +
                                "." + getServletName());
                    }
                    else {
                        wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getServletName());
                    }
                }
                else {
                    // Servlet 2.5's getContextPath available!
                    wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                            ObjectUtils.getDisplayString(sc.getContextPath()) + "/" + getServletName());
                }
            }
        }
 
        wac.setServletContext(getServletContext());
        wac.setServletConfig(getServletConfig());
        wac.setNamespace(getNamespace());
        wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
 
        // the wac environment's #initPropertySources will be called in any case when
        // the context is refreshed; do it eagerly here to ensure servlet property sources
        // are in place for use in any post-processing or initialization that occurs
        // below prior to #refresh
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment)env).initPropertySources(getServletContext(), getServletConfig());
        }
 
        postProcessWebApplicationContext(wac);
 
        applyInitializers(wac);
        //此处调用AbstractApplicationContext的refresh方法,初始化容器
        wac.refresh();
 }

(2-2)、调用DispatcherServlet的initStrategies方法

初始化HandlerMapping、HandlerAdapter、ViewResolver

protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        initHandlerMappings(context);
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        initViewResolvers(context);
        initFlashMapManager(context);
}

DispatcherServlet的initHandlerMappings方法

private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;
 
        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 {
            try {
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }
 
        // Ensure we have at least one HandlerMapping, by registering
        // a default HandlerMapping if no other mappings are found.
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

/DispatcherServlet的initHandlerAdapters方法

private void initHandlerAdapters(ApplicationContext context) {
        this.handlerAdapters = null;
 
        if (this.detectAllHandlerAdapters) {
            // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
            Map<String, HandlerAdapter> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
                // We keep HandlerAdapters in sorted order.
                OrderComparator.sort(this.handlerAdapters);
            }
        }
        else {
            try {
                HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
                this.handlerAdapters = Collections.singletonList(ha);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerAdapter later.
            }
        }
 
        // Ensure we have at least some HandlerAdapters, by registering
        // default HandlerAdapters if no other adapters are found.
        if (this.handlerAdapters == null) {
            this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");
            }
        }
 }

DispatcherServlet的initViewResolvers方法

private void initViewResolvers(ApplicationContext context) {
        this.viewResolvers = null;
 
        if (this.detectAllViewResolvers) {
            // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
            Map<String, ViewResolver> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values());
                // We keep ViewResolvers in sorted order.
                OrderComparator.sort(this.viewResolvers);
            }
        }
        else {
            try {
                ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);
                this.viewResolvers = Collections.singletonList(vr);
            }
            catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default ViewResolver later.
            }
        }
 
        // Ensure we have at least one ViewResolver, by registering
        // a default ViewResolver if no other resolvers are found.
        if (this.viewResolvers == null) {
            this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default");
            }
        }
 }

3、Spring MVC请求源码解析

Paste_Image.png

doDispatch函数,spring mvc主要处理流程

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
            HttpServletRequest processedRequest = request;  
            HandlerExecutionChain mappedHandler = null;  
            boolean multipartRequestParsed = false;  
       
            WebAsyncManager asyncManager = AsyncWebUtils.getAsyncManager(request);  
       
            try {  
                ModelAndView mv = null;  
                Exception dispatchException = null;  
       
                try {  
                    processedRequest = checkMultipart(request);  
                    multipartRequestParsed = processedRequest != request;  
       
                    // Determine handler for the current request.  
                    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.  
                    ……  
                    try {  
                        // Actually invoke the handler.  
                        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
                    }  
                    finally {  
                        if (asyncManager.isConcurrentHandlingStarted()) {  
                            return;  
                        }  
                    }  
       
                    applyDefaultViewName(request, mv);  
                    mappedHandler.applyPostHandle(processedRequest, response, mv);  
                }  
                catch (Exception ex) {  
                    dispatchException = ex;  
                }  
                processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
            }  
            ……  
            }  
            finally {  
                ……  
            }  
}

HandlerExecutionChain这个类,有两个关键的东西handler与interceptors,也即处理器对象与拦截器链表

public class HandlerExecutionChain {  
       
        private final Object handler;  
       
        private HandlerInterceptor[] interceptors;  
       
        private List<HandlerInterceptor> interceptorList;  
       
        ……  
} 

doDispatch函数中获得HandlerExecutionChain对象mappedHandler。
函数的实现非常简单:遍历已注册的HandlerMapping列表,通过HandlerMapping对象的getHandler函数获取HandlerExecutionChain对象。至于HandlerMapping中的getHandler函数如何获取HandlerExecutionChain需要的处理器与拦截器,我只能说过程是繁琐的,原理是简单的,对于处理器对象(handler)根据不同的Mapping实现,其可以根据bean配置中的urlPath或者是方法的注解来寻找,这里不再细说。

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
        for (HandlerMapping hm : this.handlerMappings) {  
            ……  
            HandlerExecutionChain handler = hm.getHandler(request);  
            if (handler != null) {  
                return handler;  
            }  
        }  
        return null;  
} 
mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 

//通过getHandlerAdapter函数获得HandlerAdapter对象ha,随着调用HandlerAdapter的handle函数,Spring MVC便开始要真的“干实事”了,所谓的“干实事”也即开始调用执行我们编写的controller(控制逻辑)了。这里以两个HandlerAdapter的实现HttpRequestHandlerAdapter与AnnotationMethodHandlerAdapter来分析这个处理流程。
HttpRequestHandlerAdapter的实现是最容易理解的,因为其handle的实现就是调用了处理器(handler)对象的handleRequest函数,借助F4看看Controller的继承体系,再看看AbstractController中handleRequest函数的实现。
至于AnnotationMethodHandlerAdapter,其实现原理也是很容易理解的,我们已经知道其就是针对采用注解方式的方法映射的,所以其handle的实现就是通过Java的反射机制找到注解对应的处理方法,并调用完成控制逻辑的执行。

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 

//视图处理。在经过handle的“干实事”后,我们得到了ModelAndView对象,也即视图对象,很自然接下来的就是视图的渲染与展示了。
视图处理时序图

Paste_Image.png
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()) {
            // We need to resolve the view name.
            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 {
            // No need to lookup: the ModelAndView object contains the actual View object.
            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() + "'");
            }
        }
 
        // Delegate to the View object for rendering.
        if (logger.isDebugEnabled()) {
            logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        try {
            view.render(mv.getModelInternal(), request, response);
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
                        getServletName() + "'", ex);
            }
            throw ex;
        }
    }

processDispatchResult主要处理异常、请求状态及触发请求完成事件,视图的渲染工作交给了render(),render()判断前面获得的ModelAndView对象是否为引用对象(也即此时有的只是视图的名称,没有获取其实例对象),如果是则需要调用resolveViewName从视图解析器获取对应的视图(View)对象,否则直接以其getView函数获取视图对象。最后,通过具体视图对象的render()函数完成视图的渲染。

Spring MVC中,在控制器处理结束并返回ModelAndView名称后,Spring会依次调用Spring容器中所注册的视图解析器(ViewResolver),来查找符合条件的视图。对于给定的视图名称,只需调用ViewResolver的resolveViewName接口即可获取其视图对象。Spring中默认提供了许多的视图解析器,当然,还有对应类型的视图对象。
如针对JSP提供的InternalResourceViewResolver与InternalResourceView,时序图。

Paste_Image.png Paste_Image.png

refer:
http://blog.csdn.net/zjw10wei321/article/details/40145241

上一篇下一篇

猜你喜欢

热点阅读