Spring MVC中的DispatcherServlet实现逻辑视图到实际视图映射的过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lu__peng/article/details/80600778

Spring MVC的大致流程:

  1. 在Spring MVC框架下,请求通常会被拦截交给DispatcherServlet进行处理,DispatcherServlet会根据配置信息(HandlerMapping)调用合适的handler进行处理;
  2. handler会进行相关业务逻辑处理,必要的时候会通过dao层与数据库进行数据交互;
    处理完成之后会将处理结果封装到一个Model中,这个Model通常包含两部分信息:视图名信息以及处理结果数据信息;
  3. DispatcherServlet会根据handler返回的Model调用对应的视图;
  4. 视图负责将Model中的数据信息以合适的方式展现给用户。

Spring MVC的流程基本上就如上面所述,本次主要对3,4步进行介绍

1 初始化视图解析器

在启动web容器时,会根据web.xml配置文件,在指定时间启动Spring MVC,此时会进行相关初始化工作:

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

注意语句this.initViewResolvers(context);语句,Spring MVC会根据配置文件初始化对应视图解析器,并所有的视图解析器实例对象放入到一个List集合中,用于后面的视图解析;

private void initViewResolvers(ApplicationContext context) {
    this.viewResolvers = null;
    if (this.detectAllViewResolvers) {
        Map<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.viewResolvers = new ArrayList(matchingBeans.values());
            AnnotationAwareOrderComparator.sort(this.viewResolvers);
        }
    } else {
        try {
            ViewResolver vr = (ViewResolver)context.getBean("viewResolver", ViewResolver.class);
            this.viewResolvers = Collections.singletonList(vr);
        } catch (NoSuchBeanDefinitionException var3) {
            ;
        }
 }

2 DispatcherServlet转发请求获得处理结果

这里没有详细说,实际上是请求被拦截,交给DispatcherServlet,DispatcherServlet通过doDispatcher()进行转发,并获得处理结果ModelAndView

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException); //对ModelAndView进行进一步处理
...
this.render(mv, request, response); //调用该方法来处理mv

3 获取视图对象

先看看render()方法:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
       /**省略代码**/
       View view;

       /**省略代码**/   //1
       view = this.resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);

       /**省略代码**/  //2
       view.render(mv.getModelInternal(), request, response);

}

3.1 获取视图对象

通过代码1 获取视图对象,具体就是通过视图解析器对ModelAndView进行解析,将逻辑视图映射到实际视图上:

//该方法,会调用视图解析器的List集合,从中获取能够解析mv的视图解析器
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
        Iterator var5 = this.viewResolvers.iterator();
        View view;
        do {
            if (!var5.hasNext()) {
                return null;
            }

            ViewResolver viewResolver = (ViewResolver)var5.next();
            //对视图进行解析,并返回视图对象
            view = viewResolver.resolveViewName(viewName, locale);
        } while(view == null);

        return view;
    }

接下来看看试图解析器是如何对mv进行解析的:

public View resolveViewName(String viewName, Locale locale) throws Exception {
        if (!this.isCache()) {
            return this.createView(viewName, locale);
        } else {
            Object cacheKey = this.getCacheKey(viewName, locale);
            View view = (View)this.viewAccessCache.get(cacheKey);
            if (view == null) {
                Map var5 = this.viewCreationCache;
                synchronized(this.viewCreationCache) {
                    view = (View)this.viewCreationCache.get(cacheKey);
                    if (view == null) {
                        //如果缓存Map集合中没有对应的视图对象的时候,才进行mv的解析
                        view = this.createView(viewName, locale);
                        if (view == null && this.cacheUnresolved) {
                            view = UNRESOLVED_VIEW;
                        }

                        if (view != null) {
                            this.viewAccessCache.put(cacheKey, view);
                            this.viewCreationCache.put(cacheKey, view);
                            if (this.logger.isTraceEnabled()) {
                                this.logger.trace("Cached view [" + cacheKey + "]");
                            }
                        }
                    }
                }
            }

            return view != UNRESOLVED_VIEW ? view : null;
        }
    }

上面这个方法需要重点说明一下:该方法属于AbstractCachingViewResolver类,在该类中定义了这样一个成员变量:

private final Map<Object, View> viewAccessCache = new ConcurrentHashMap(1024);

该线程盖全集合用于缓存已经解析过的视图对象,以视图名(逻辑视图名)和视图对象的key-value形式存放,一遍复用。
如果缓存Map集合中没有对应的视图对象的时候,才进行mv的解析,并将解析的视图对象存放在缓存集合中

再看具体的解析过程:

//viewNmae是逻辑视图名,经过太多太多代码了,容易忘记
protected View createView(String viewName, Locale locale) throws Exception {
        if (!this.canHandle(viewName, locale)) {
            return null;
        } else {
            String forwardUrl;
            //对应集中转发情形
            if (viewName.startsWith("redirect:")) {
                forwardUrl = viewName.substring("redirect:".length());
                RedirectView view = new RedirectView(forwardUrl, this.isRedirectContextRelative(), this.isRedirectHttp10Compatible());
                view.setHosts(this.getRedirectHosts());
                return this.applyLifecycleMethods(viewName, view);
            } else if (viewName.startsWith("forward:")) {
                forwardUrl = viewName.substring("forward:".length());
                return new InternalResourceView(forwardUrl);
            } else {
                //我们是最普通的方式,调用该方法
                return super.createView(viewName, locale);
            }
        }
    }

然后会依次代用createView() –> loadView() –>buildView()
最终会调用UrlBasedViewResolver类中的buildView方法,在该方法中会根据配置文件中定义的prefix和suffix,设置该View对象的url

protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        AbstractUrlBasedView view = (AbstractUrlBasedView)BeanUtils.instantiateClass(this.getViewClass());
        //设置该View对象的url
        view.setUrl(this.getPrefix() + viewName + this.getSuffix());

        /**省略代码**/
        //最终返回我们需要的view对象,该对象中包含实际视图的位置:url
        return view;
    }

3.2 得到实际视图

看第三章开头的代码,执行完代码1之后会接着执行代码2,实现具体的视图映射功能。通过获得的view对象调用render()方法,该方法又会滴啊用renderMergedOutputModel()方法:

protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        //request就是本次请求信息,,把mv中的数据model封装在request对象里,setAttribute()方法设定
        this.exposeModelAsRequestAttributes(model, request);
        this.exposeHelpers(request);
        //实际视图名
        String dispatcherPath = this.prepareForRendering(request, response);

        //Request中的forward()方法
        RequestDispatcher rd = this.getRequestDispatcher(request, dispatcherPath);
       rd.forward(request, response); 
       /**
       *也有可能是rd.include()方法;
       **/
}

最终是借助于Request中的forward方法找到实际视图,转发给用户。

猜你喜欢

转载自blog.csdn.net/lu__peng/article/details/80600778