上一节看完了ViewResolver解析视图名产生View的过程,这就到了最后的流程View执行它的render方法,实现我们到视图的跳转,对不起,前两篇都忘了说了,主要是学习札记,整个过程解释的不多其实,如果愿意看下去的朋友,我只是提供看过程的思路,spring给的注释都很通俗易懂,加上我的部分解释,应该很清楚的我想。
view.render(mv.getModelInternal(), request, response);
下面来看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); } // Consolidate static and dynamic model attributes. Map<String, Object> mergedModel = new HashMap<String, Object>(this.staticAttributes.size() + (model != null ? model.size() : 0)); mergedModel.putAll(this.staticAttributes); if (model != null) { mergedModel.putAll(model); } // Expose RequestContext? if (this.requestContextAttribute != null) { mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel)); } prepareResponse(request, response); renderMergedOutputModel(mergedModel, request, response); }
model参数就是ModelAndView对象mv中存放
/** Model Map */ private ModelMap model;
ModelMap:
public class ModelMap extends LinkedHashMap<String, Object>
render方法的首要作用就是将model的键值对属性和View类中的属性staticAttributes整合在一起,什么是静态属性呢~类似视图的页眉、页脚的固定信息等,它们可以固定不变,所以可以直接写在配置文件中。
在***-servlet.xml中,我们配置View的bean时,我们可以为视图定义
<property name="attributesCSV"> <value>author=sha0k,copyRight=iteye</value> </property>
而这种固定的信息就将被解析为View类的staticAttributtes
/** * Set static attributes as a CSV string. * Format is: attname0={value1},attname1={value1} * <p>"Static" attributes are fixed attributes that are specified in * the View instance configuration. "Dynamic" attributes, on the other hand, * are values passed in as part of the model. */ public void setAttributesCSV(String propString) throws IllegalArgumentException { if (propString != null) { StringTokenizer st = new StringTokenizer(propString, ","); while (st.hasMoreTokens()) { String tok = st.nextToken(); int eqIdx = tok.indexOf("="); if (eqIdx == -1) { throw new IllegalArgumentException("Expected = in attributes CSV string '" + propString + "'"); } if (eqIdx >= tok.length() - 2) { throw new IllegalArgumentException( "At least 2 characters ([]) required in attributes CSV string '" + propString + "'"); } String name = tok.substring(0, eqIdx); String value = tok.substring(eqIdx + 1); // Delete first and last characters of value: { and } value = value.substring(1); value = value.substring(0, value.length() - 1); addStaticAttribute(name, value); } } }
通读这个方法的代码,就可以看到它对property value string的解析过程,注意最后一行代码addStaticAttributte。。。
public void addStaticAttribute(String name, Object value) { this.staticAttributes.put(name, value); }
到此就很好理解staticAttributtes了。。。。
createRequestContext
这里请先跳过,我也没懂,源码涉及了另外一个类。。。
/** * 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"); } }
这个方法的主要内容在于generatesDownloadContent:
/** * Return whether this view generates download content * (typically binary content like PDF or Excel files). * <p>The default implementation returns <code>false</code>. Subclasses are * encouraged to return <code>true</code> here if they know that they are * generating download content that requires temporary caching on the * client side, typically via the response OutputStream. * @see #prepareResponse * @see javax.servlet.http.HttpServletResponse#getOutputStream() */ protected boolean generatesDownloadContent() { return false; }
根据注释就可以看出,这段准备response的代码其实就做了一件事情,判断我们的响应输出是不是下载内容~~~download content!!!这就算懂了,继续了哦
renderMergedOutputModel这个方法,AbstractView类并没有实现它,在它里面是一个抽象方法,它的子类即InternalResourceView的父类AbstractUrlBasedView类也没有实现,所以我们就将代码的注意力跳转到InternalResourceView的renderMergedOutputModel方法中。
/** * Render the internal resource given the specified model. * This includes setting the model as request attributes. */ @Override protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Determine which request handle to expose to the RequestDispatcher. HttpServletRequest requestToExpose = getRequestToExpose(request); // Expose the model object as request attributes. exposeModelAsRequestAttributes(model, requestToExpose); // Expose helpers as request attributes, if any. exposeHelpers(requestToExpose); // Determine the path for the request dispatcher. String dispatcherPath = prepareForRendering(requestToExpose, response); // Obtain a RequestDispatcher for the target resource (typically a JSP). RequestDispatcher rd = requestToExpose.getRequestDispatcher(dispatcherPath); if (rd == null) { throw new ServletException( "Could not get RequestDispatcher for [" + getUrl() + "]: check that this file exists within your WAR"); } // If already included or response already committed, perform include, else forward. if (useInclude(requestToExpose, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.include(requestToExpose, response); } else { // Note: The forwarded resource is supposed to determine the content type itself. exposeForwardRequestAttributes(requestToExpose); if (logger.isDebugEnabled()) { logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.forward(requestToExpose, response); } }
来说重点了,我跳过的要么是根据注释就能懂的,跳到源码就更容易了,要么是根本看不懂的。。。。嘿嘿,下面的又回到了AbstractView类的方法中了,因为它的子类并没有重写它
/** * Expose the model objects in the given map as request attributes. * Names will be taken from the model Map. * This method is suitable for all resources reachable by {@link javax.servlet.RequestDispatcher}. * @param model Map of model objects to expose * @param request current HTTP request */ protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception { for (Map.Entry<String, Object> entry : model.entrySet()) { String modelName = entry.getKey(); Object modelValue = entry.getValue(); if (modelValue != null) { request.setAttribute(modelName, modelValue); if (logger.isDebugEnabled()) { logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() + "] to request in view with name '" + getBeanName() + "'"); } } else { request.removeAttribute(modelName); if (logger.isDebugEnabled()) { logger.debug("Removed model object '" + modelName + "' from request in view with name '" + getBeanName() + "'"); } } } }
根据上面的代码通读后,可以看懂,原来跟字面意思一样就是说暴露Model,暴露给谁呢?暴露给HttpSerlvetRequest,也就是把Model里的属性全部加入到request中,原来我们能在jsp页面用request访问我们添加到Model里的属性就是这里实现的啊~~~~哈哈
下面就不用我解释了,include和forwad,serlvet里的基本功~~~是不是流程明白了其实不难呢?到此整个流程就完成了,以后就要研究spring惊人的设计模式和方法了,重中之重在于 先把关键的类得看懂和看明白,下一节,就先看HandlerMapping的树结构吧