zoukankan      html  css  js  c++  java
  • spring mvc view 相关

      由于前段时间一直在成都出差加上心情比较乱,很长时间没有更新博客。

      最近一个人负责的成都项目进展比较顺利,基本只剩下一些故障单的修复,所以压力不是很大,所以近段时间就是解决一下故障单,然后剩下的就是为主体项目4.0部分前端资源如何整合进行学习和思考,初步打算是使用requireJS,AMD标准的JS模块化还是比较适合前台的加载,这些容后续再表。

      一直对框架上对模版的加载比较好奇,所以今天就看了下部分源码,以下内容如有理解不当或者错误,欢迎指正。

      java web容器,由服务器向浏览器客户端写回页面无非就是向response中的outputstream流中写入内容。而我们使用各种各样的模板时(比如freemarker或者jsp),首先需要在spring的主要的dispatchServlet对于的配置文件中配置模板解析的bean。常见的配置如下:

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
            <property name="defaultEncoding" value="utf-8" />  
            <!-- 文件大小最大值 -->
            <property name="maxUploadSize" value="10485760000" />  
            <!-- 内存中的最大值 -->
            <property name="maxInMemorySize" value="40960" />  
    </bean> 

      当我们对SpringMVC控制的资源发起请求时,这些请求都会被SpringMVC 的DispatcherServlet处理,接着Spring会分析看哪一个HandlerMapping定义的所有请求映射中存 在对该请求的最合理的映射。然后通过该HandlerMapping取得其对应的Handler,接着再通过相应 的HandlerAdapter处理该Handler。HandlerAdapter在对Handler进行处理之后会返回一个 ModelAndView对象。在获得了ModelAndView对象之后,Spring就需要把该View渲染给用户,即返 回给浏览器。在这个渲染的过程中,发挥作用的就是ViewResolver和View。当Handler返回的 ModelAndView中不包含真正的视图,只返回一个逻辑视图名称的时候,ViewResolver就会把该逻 辑视图名称解析为真正的视图View对象。View是真正进行视图渲染,把结果返回给浏览器的。

      ViewResolver和View介绍 SpringMVC用于处理视图最重要的两个接口是ViewResolver和View。ViewResolver的主要作用 是把一个逻辑上的视图名称解析为一个真正的视图,SpringMVC中用于把View对象呈现给客户端的 是View对象本身,而ViewResolver只是把逻辑视图名称解析为对象的View对象。View接口的主要 作用是用于处理视图,然后返回给客户端。

      view接口中声明了如下的方法

    /**
         * Render the view given the specified model.
         * <p>The first step will be preparing the request: In the JSP case,
         * this would mean setting model objects as request attributes.
         * The second step will be the actual rendering of the view,
         * for example including the JSP via a RequestDispatcher.
         * @param model Map with name Strings as keys and corresponding model
         * objects as values (Map can also be {@code null} in case of empty model)
         * @param request current HTTP request
         * @param response HTTP response we are building
         * @throws Exception if rendering failed
         */
        void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;

    即实现该方法即可。(在dispatchServlet的doservice方法中调用了私有方法processDispatchResult, 该私有方法中调用了render(mv, request, response) ,此处的mv为controller层返回的ModelAndView类的一个实例,在该render方法中有一行view.render(mv.getModelInternal(), request, response);,即在该方法中会实现写入response中)。

    我们随意找一个实现 FreeMarkerView (public class FreeMarkerView extends AbstractTemplateView , AbstractTemplateView extends AbstractUrlBasedView  , AbstractUrlBasedView extends AbstractView implements InitializingBean ,

     class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware 可见一个view还是很复杂的,隐藏的这么深。)

    我们发现在AbstractView 类中实现了render方法

    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            if (logger.isTraceEnabled()) {
                logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
                    " and static attributes " + this.staticAttributes);
            }
    
            Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
            prepareResponse(request, response);
            renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
    }

    createMergedOutputModel方法主要是将一些属性填充到Map中,prepareResponse内容如下,主要是对response头进行了一些属性设置

    /**
         * Prepare the given response for rendering.
         * <p>The default implementation applies a workaround for an IE bug
         * when sending download content via HTTPS.
         * @param request current HTTP request
         * @param response current HTTP response
         */
        protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
            if (generatesDownloadContent()) {
                response.setHeader("Pragma", "private");
                response.setHeader("Cache-Control", "private, must-revalidate");
            }
    }

    重点看renderMergedOutputModel函数,在AbstractTemplateView类中对该方法进行了实现

    protected final void renderMergedOutputModel(
                Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    
            if (this.exposeRequestAttributes) {
                for (Enumeration<String> en = request.getAttributeNames(); en.hasMoreElements();) {
                    String attribute = en.nextElement();
                    if (model.containsKey(attribute) && !this.allowRequestOverride) {
                        throw new ServletException("Cannot expose request attribute '" + attribute +
                            "' because of an existing model object of the same name");
                    }
                    Object attributeValue = request.getAttribute(attribute);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Exposing request attribute '" + attribute +
                                "' with value [" + attributeValue + "] to model");
                    }
                    model.put(attribute, attributeValue);
                }
            }
    
            if (this.exposeSessionAttributes) {
                HttpSession session = request.getSession(false);
                if (session != null) {
                    for (Enumeration<String> en = session.getAttributeNames(); en.hasMoreElements();) {
                        String attribute = en.nextElement();
                        if (model.containsKey(attribute) && !this.allowSessionOverride) {
                            throw new ServletException("Cannot expose session attribute '" + attribute +
                                "' because of an existing model object of the same name");
                        }
                        Object attributeValue = session.getAttribute(attribute);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Exposing session attribute '" + attribute +
                                    "' with value [" + attributeValue + "] to model");
                        }
                        model.put(attribute, attributeValue);
                    }
                }
            }
    
            if (this.exposeSpringMacroHelpers) {
                if (model.containsKey(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE)) {
                    throw new ServletException(
                            "Cannot expose bind macro helper '" + SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE +
                            "' because of an existing model object of the same name");
                }
                // Expose RequestContext instance for Spring macros.
                model.put(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE,
                        new RequestContext(request, response, getServletContext(), model));
            }
    
            applyContentType(response);
    
            renderMergedTemplateModel(model, request, response);
        }

    注意最后一行的renderMergedTemplateModel函数,该函数在FreeMarkerView 类中进行了实现,调用了doRender方法

    protected void doRender(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            // Expose model to JSP tags (as request attributes).
            exposeModelAsRequestAttributes(model, request);
            // Expose all standard FreeMarker hash models.
            SimpleHash fmModel = buildTemplateModel(model, request, response);
    
            if (logger.isDebugEnabled()) {
                logger.debug("Rendering FreeMarker template [" + getUrl() + "] in FreeMarkerView '" + getBeanName() + "'");
            }
            // Grab the locale-specific version of the template.
            Locale locale = RequestContextUtils.getLocale(request);
            processTemplate(getTemplate(locale), fmModel, response);
        }

    其中processTemplate(getTemplate(locale), fmModel, response);中只有一行 template.process(model, response.getWriter());

    关于freemarker的jar包中的

    这是一个很经典的模板引擎的执行流程:

    • Configuration可以理解为一个工厂,它负责产生一个对外接口Template类。它首先会从cache中查找是否已经有编译好的Template,如果不存在,则对模板进行编译。
    • Template实际上是一个带执行语义的语法树,树的节点是TemplateObject。
    • FMParser是javacc生成的语法解析类,它最终输出是以FMParser.root()为根的语法树。
    • dataModel是外部对模板引擎的数据输入,它会被转化为TemplateModel,并代入模板的渲染过程。
    • 最后的步骤是根据数据,遍历编译好的语法树,并输出结果,这一步的入口时TemplateElement.accept()。

    以上即是所有的分析。

    下一篇应该会写freemarker其中的标签是如何解析的

      

  • 相关阅读:
    Scrum Meeting 11.11
    Scrum Meeting 11.10
    Scrum Meeting 11.09
    Scrum Meeting 11.08
    Scrum Meeting 11.07
    Scrum Meeting 11.06
    Scrum Meeting 11.05
    Scrum Meeting 11.04
    团队博客-应用功能说明书
    Scrum Meeting 11.03
  • 原文地址:https://www.cnblogs.com/vikeria/p/5161936.html
Copyright © 2011-2022 走看看