美文网首页
Spring系列之MVC

Spring系列之MVC

作者: 康康不遛猫 | 来源:发表于2017-05-11 00:38 被阅读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

    相关文章

      网友评论

          本文标题:Spring系列之MVC

          本文链接:https://www.haomeiwen.com/subject/yjlntxtx.html