上一节看完了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的树结构吧