springmvc view 设计分析

 

总结:

ViewResolver 如果要改需要自己注入到容器中并进行修改, springmvc使用的是InterResourceViewResover
view不需要自己改,是springmvc根据return返回值选的

 

前一篇只是在debug, 有点乱。所以在这里重新解释看看。

既然看到有ModelAndView直接跳转jsp的, 有请求转发的,有重定向的,这里整体是怎么设计的:  (@ResponseBody的在此不作展开)

HiController:

@Controller
public class HiController {
    @RequestMapping("/hi")
    public ModelAndView getHi() {
        ModelAndView mav = new ModelAndView("me");
        return mav;
    }

    @RequestMapping("/yes")
    public String forwardYes() {
        return "forward:patch";
    }

    @RequestMapping("/no")
    public String RedirectNo() {
        return "redirect:patch";
    }

    @ResponseBody
    @RequestMapping("/patch")
    public String redirectNo() {
        return "from forward or redirect request";     // 这种情况没有view,在这里不讨论
    }
}

 





 

 

 主要代码:

DispatcherServlet.doDispatch()里的:

 

 

DispatcherServlet.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 != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
        response.setLocale(locale);

        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            // We need to resolve the view name.
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);     // 1
            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.isTraceEnabled()) {
            logger.trace("Rendering view [" + view + "] ");
        }
        try {
            if (mv.getStatus() != null) {
                response.setStatus(mv.getStatus().value());
            }
            view.render(mv.getModelInternal(), request, response);    // 2
        }
        catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Error rendering view [" + view + "]", ex);
            }
            throw ex;
        }
    }
1. view = resolveViewName()会根据不同的路径生成不同的view, return mav 会返回JstlView, return "forward:/patch" 会返回InternalResourceView, return "direct:/patch" 会返回IndirectView
2. 不同的view去走不同的view.render(),  根据不同的view重写abstract void renderMergedOutputModel方法





 

 
再来看是如何生成不同的view:
view = resolveViewName() 进去,走到

DiapatcherServlet先有ViewResolver这个,用来生成不同的view
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
            Locale locale, HttpServletRequest request) throws Exception {

        if (this.viewResolvers != null) {
            for (ViewResolver viewResolver : this.viewResolvers) {
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
        }
        return null;
    }

ViewResolver 接口只有一个方法

public interface ViewResolver {
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;

}

 

要配置具体的视图解析器,springMVC中使用的是InterResourceViewResover, InterResourceViewResover 和他的父类UrlBasedViewResolver中都没有重写resolveViewName方法,再上一层的父类AbstractCahingViewResolver实现了resolveViewName方法

上一篇看过的,很眼熟吧

AbstractCahingViewResolver:

    @Override
    @Nullable
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        if (!isCache()) {
            return createView(viewName, locale);
        }
        else {
            Object cacheKey = getCacheKey(viewName, locale);
            View view = this.viewAccessCache.get(cacheKey);
            if (view == null) {
                synchronized (this.viewCreationCache) {
                    view = this.viewCreationCache.get(cacheKey);
                    if (view == null) {
                        // Ask the subclass to create the View object.
                        view = createView(viewName, locale);
                        if (view == null && this.cacheUnresolved) {
                            view = UNRESOLVED_VIEW;
                        }
                        if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
                            this.viewAccessCache.put(cacheKey, view);
                            this.viewCreationCache.put(cacheKey, view);
                        }
                    }
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace(formatKey(cacheKey) + "served from cache");
                }
            }
            return (view != UNRESOLVED_VIEW ? view : null);
        }
    }

 

InterResourceViewResover中没有createView方法,所以是调用它父类UrlBasedViewResolver的createView方法:

UrlBasedViewResolver:

    @Override
    protected View createView(String viewName, Locale locale) throws Exception {
        // If this resolver is not supposed to handle the given view,
        // return null to pass on to the next resolver in the chain.
        if (!canHandle(viewName, locale)) {
            return null;
        }

        // Check for special "redirect:" prefix.
        if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
            String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
            RedirectView view = new RedirectView(redirectUrl,
                    isRedirectContextRelative(), isRedirectHttp10Compatible());
            String[] hosts = getRedirectHosts();
            if (hosts != null) {
                view.setHosts(hosts);
            }
            return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);   // return "direct:/patch"在这里构造view
        }

        // Check for special "forward:" prefix.
        if (viewName.startsWith(FORWARD_URL_PREFIX)) {
            String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
            InternalResourceView view = new InternalResourceView(forwardUrl);
            return applyLifecycleMethods(FORWARD_URL_PREFIX, view);     // return "forward:/patch" 在这里构造view
        } 

        // Else fall back to superclass implementation: calling loadView.
        return super.createView(viewName, locale);                   //  return mav  在这里构造view
    }

 

 


关于ViewResolver的代码执行顺序, 前面分析那么多,这里再打断点快速验证一下:
进DispatcherServlet的doDispatch看到就是这个解析器:

 

断点放在这里,

 

 

 然后下一步:

 







posted @ 2020-10-18 13:39  圣金巫灵  阅读(188)  评论(0编辑  收藏  举报