一、前言
关于springframework的spring-webmvc包定义org.springframework.web.servlet.View视图接口、org.springframework.web.servlet.ViewRendererServlet模型servlet服务端实现类,通过各种pdf、velocity、json、freemarker、tile、xml等视图场景实现定义view接口,再通过HttpServlet的ViewRendererServlet子类实现Modeal数据和展示模板view的解析合成。
二、源码说明
1. View接口<-AbstractView抽象类 <-AbstractPdfView的pdf视图类
package org.springframework.web.servlet;@b@@b@import java.util.Map;@b@import javax.servlet.http.HttpServletRequest;@b@import javax.servlet.http.HttpServletResponse;@b@@b@public abstract interface View@b@{@b@ public static final String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";@b@ public static final String PATH_VARIABLES = View.class.getName() + ".pathVariables";@b@ public static final String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";@b@@b@ public abstract String getContentType();@b@@b@ public abstract void render(Map<String, ?> paramMap, HttpServletRequest paramHttpServletRequest, HttpServletResponse paramHttpServletResponse)@b@ throws Exception;@b@}
package org.springframework.web.servlet.view;@b@@b@import java.io.ByteArrayOutputStream;@b@import java.io.IOException;@b@import java.util.Collections;@b@import java.util.HashMap;@b@import java.util.Map;@b@import java.util.Properties;@b@import java.util.StringTokenizer;@b@import javax.servlet.ServletOutputStream;@b@import javax.servlet.http.HttpServletRequest;@b@import javax.servlet.http.HttpServletResponse;@b@@b@import org.springframework.beans.factory.BeanNameAware;@b@import org.springframework.util.CollectionUtils;@b@import org.springframework.web.context.support.WebApplicationObjectSupport;@b@import org.springframework.web.servlet.View;@b@import org.springframework.web.servlet.support.RequestContext;@b@@b@ @b@public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {@b@@b@ /** Default content type. Overridable as bean property. */@b@ public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=ISO-8859-1";@b@@b@ /** Initial size for the temporary output byte array (if any) */@b@ private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 4096;@b@@b@@b@ private String beanName;@b@@b@ private String contentType = DEFAULT_CONTENT_TYPE;@b@@b@ private String requestContextAttribute;@b@@b@ /** Map of static attributes, keyed by attribute name (String) */@b@ private final Map<String, Object> staticAttributes = new HashMap<String, Object>();@b@@b@ /** Whether or not the view should add path variables in the model */@b@ private boolean exposePathVariables = true;@b@@b@ /**@b@ * Set the view's name. Helpful for traceability.@b@ * <p>Framework code must call this when constructing views.@b@ */@b@ public void setBeanName(String beanName) {@b@ this.beanName = beanName;@b@ }@b@@b@ /**@b@ * Return the view's name. Should never be <code>null</code>,@b@ * if the view was correctly configured.@b@ */@b@ public String getBeanName() {@b@ return this.beanName;@b@ }@b@@b@ /**@b@ * Set the content type for this view.@b@ * Default is "text/html;charset=ISO-8859-1".@b@ * <p>May be ignored by subclasses if the view itself is assumed@b@ * to set the content type, e.g. in case of JSPs.@b@ */@b@ public void setContentType(String contentType) {@b@ this.contentType = contentType;@b@ }@b@@b@ /**@b@ * Return the content type for this view.@b@ */@b@ public String getContentType() {@b@ return this.contentType;@b@ }@b@@b@ /**@b@ * Set the name of the RequestContext attribute for this view.@b@ * Default is none.@b@ */@b@ public void setRequestContextAttribute(String requestContextAttribute) {@b@ this.requestContextAttribute = requestContextAttribute;@b@ }@b@@b@ /**@b@ * Return the name of the RequestContext attribute, if any.@b@ */@b@ public String getRequestContextAttribute() {@b@ return this.requestContextAttribute;@b@ }@b@@b@ /**@b@ * Set static attributes as a CSV string.@b@ * Format is: attname0={value1},attname1={value1}@b@ * <p>"Static" attributes are fixed attributes that are specified in@b@ * the View instance configuration. "Dynamic" attributes, on the other hand,@b@ * are values passed in as part of the model.@b@ */@b@ public void setAttributesCSV(String propString) throws IllegalArgumentException {@b@ if (propString != null) {@b@ StringTokenizer st = new StringTokenizer(propString, ",");@b@ while (st.hasMoreTokens()) {@b@ String tok = st.nextToken();@b@ int eqIdx = tok.indexOf("=");@b@ if (eqIdx == -1) {@b@ throw new IllegalArgumentException("Expected = in attributes CSV string '" + propString + "'");@b@ }@b@ if (eqIdx >= tok.length() - 2) {@b@ throw new IllegalArgumentException(@b@ "At least 2 characters ([]) required in attributes CSV string '" + propString + "'");@b@ }@b@ String name = tok.substring(0, eqIdx);@b@ String value = tok.substring(eqIdx + 1);@b@@b@ // Delete first and last characters of value: { and }@b@ value = value.substring(1);@b@ value = value.substring(0, value.length() - 1);@b@@b@ addStaticAttribute(name, value);@b@ }@b@ }@b@ }@b@@b@ @b@ public void setAttributes(Properties attributes) {@b@ CollectionUtils.mergePropertiesIntoMap(attributes, this.staticAttributes);@b@ }@b@@b@ @b@ public void setAttributesMap(Map<String, ?> attributes) {@b@ if (attributes != null) {@b@ for (Map.Entry<String, ?> entry : attributes.entrySet()) {@b@ addStaticAttribute(entry.getKey(), entry.getValue());@b@ }@b@ }@b@ }@b@@b@ @b@ public Map<String, Object> getAttributesMap() {@b@ return this.staticAttributes;@b@ }@b@@b@ @b@ public void addStaticAttribute(String name, Object value) {@b@ this.staticAttributes.put(name, value);@b@ }@b@@b@ @b@ public Map<String, Object> getStaticAttributes() {@b@ return Collections.unmodifiableMap(this.staticAttributes);@b@ }@b@@b@ @b@ public void setExposePathVariables(boolean exposePathVariables) {@b@ this.exposePathVariables = exposePathVariables;@b@ }@b@@b@ /**@b@ * Returns the value of the flag indicating whether path variables should be added to the model or not.@b@ */@b@ public boolean isExposePathVariables() {@b@ return exposePathVariables;@b@ }@b@@b@ @b@ public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {@b@ if (logger.isTraceEnabled()) {@b@ logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +@b@ " and static attributes " + this.staticAttributes);@b@ }@b@ @b@ Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);@b@@b@ prepareResponse(request, response);@b@ renderMergedOutputModel(mergedModel, request, response);@b@ }@b@@b@ @b@ protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest request,@b@ HttpServletResponse response) {@b@ @SuppressWarnings("unchecked")@b@ Map<String, Object> pathVars = this.exposePathVariables ?@b@ (Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null;@b@@b@ // Consolidate static and dynamic model attributes.@b@ int size = this.staticAttributes.size();@b@ size += (model != null) ? model.size() : 0;@b@ size += (pathVars != null) ? pathVars.size() : 0;@b@ Map<String, Object> mergedModel = new HashMap<String, Object>(size);@b@ mergedModel.putAll(this.staticAttributes);@b@ if (pathVars != null) {@b@ mergedModel.putAll(pathVars);@b@ }@b@ if (model != null) {@b@ mergedModel.putAll(model);@b@ }@b@@b@ // Expose RequestContext?@b@ if (this.requestContextAttribute != null) {@b@ mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel));@b@ }@b@ @b@ return mergedModel;@b@ }@b@@b@ @b@ protected RequestContext createRequestContext(@b@ HttpServletRequest request, HttpServletResponse response, Map<String, Object> model) {@b@@b@ return new RequestContext(request, response, getServletContext(), model);@b@ }@b@@b@ @b@ protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {@b@ if (generatesDownloadContent()) {@b@ response.setHeader("Pragma", "private");@b@ response.setHeader("Cache-Control", "private, must-revalidate");@b@ }@b@ }@b@ @b@ protected boolean generatesDownloadContent() {@b@ return false;@b@ }@b@ @b@ protected abstract void renderMergedOutputModel(@b@ Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;@b@@b@@b@ @b@ protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {@b@ for (Map.Entry<String, Object> entry : model.entrySet()) {@b@ String modelName = entry.getKey();@b@ Object modelValue = entry.getValue();@b@ if (modelValue != null) {@b@ request.setAttribute(modelName, modelValue);@b@ if (logger.isDebugEnabled()) {@b@ logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +@b@ "] to request in view with name '" + getBeanName() + "'");@b@ }@b@ }@b@ else {@b@ request.removeAttribute(modelName);@b@ if (logger.isDebugEnabled()) {@b@ logger.debug("Removed model object '" + modelName +@b@ "' from request in view with name '" + getBeanName() + "'");@b@ }@b@ }@b@ }@b@ }@b@@b@ @b@ protected ByteArrayOutputStream createTemporaryOutputStream() {@b@ return new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE);@b@ }@b@@b@ @b@ protected void writeToResponse(HttpServletResponse response, ByteArrayOutputStream baos) throws IOException {@b@ // Write content type and also length (determined via byte array).@b@ response.setContentType(getContentType());@b@ response.setContentLength(baos.size());@b@@b@ // Flush byte array to servlet output stream.@b@ ServletOutputStream out = response.getOutputStream();@b@ baos.writeTo(out);@b@ out.flush();@b@ }@b@@b@@b@ @Override@b@ public String toString() {@b@ StringBuilder sb = new StringBuilder(getClass().getName());@b@ if (getBeanName() != null) {@b@ sb.append(": name '").append(getBeanName()).append("'");@b@ }@b@ else {@b@ sb.append(": unnamed");@b@ }@b@ return sb.toString();@b@ }@b@@b@}
package org.springframework.web.servlet.view.document;@b@@b@import java.io.ByteArrayOutputStream;@b@import java.io.OutputStream;@b@import java.util.Map;@b@import javax.servlet.http.HttpServletRequest;@b@import javax.servlet.http.HttpServletResponse;@b@@b@import com.lowagie.text.Document;@b@import com.lowagie.text.DocumentException;@b@import com.lowagie.text.PageSize;@b@import com.lowagie.text.pdf.PdfWriter;@b@@b@import org.springframework.web.servlet.view.AbstractView;@b@ @b@ @b@public abstract class AbstractPdfView extends AbstractView {@b@@b@ /**@b@ * This constructor sets the appropriate content type "application/pdf".@b@ * Note that IE won't take much notice of this, but there's not a lot we@b@ * can do about this. Generated documents should have a ".pdf" extension.@b@ */@b@ public AbstractPdfView() {@b@ setContentType("application/pdf");@b@ }@b@@b@@b@ @Override@b@ protected boolean generatesDownloadContent() {@b@ return true;@b@ }@b@@b@ @Override@b@ protected final void renderMergedOutputModel(@b@ Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {@b@@b@ // IE workaround: write into byte array first.@b@ ByteArrayOutputStream baos = createTemporaryOutputStream();@b@@b@ // Apply preferences and build metadata.@b@ Document document = newDocument();@b@ PdfWriter writer = newWriter(document, baos);@b@ prepareWriter(model, writer, request);@b@ buildPdfMetadata(model, document, request);@b@@b@ // Build PDF document.@b@ document.open();@b@ buildPdfDocument(model, document, writer, request, response);@b@ document.close();@b@@b@ // Flush to HTTP response.@b@ writeToResponse(response, baos);@b@ }@b@@b@ @b@ protected Document newDocument() {@b@ return new Document(PageSize.A4);@b@ }@b@@b@ @b@ protected PdfWriter newWriter(Document document, OutputStream os) throws DocumentException {@b@ return PdfWriter.getInstance(document, os);@b@ }@b@@b@ @b@ protected void prepareWriter(Map<String, Object> model, PdfWriter writer, HttpServletRequest request)@b@ throws DocumentException {@b@@b@ writer.setViewerPreferences(getViewerPreferences());@b@ }@b@@b@ @b@ protected int getViewerPreferences() {@b@ return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage;@b@ }@b@@b@ @b@ protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) {@b@ }@b@@b@ @b@ protected abstract void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer,@b@ HttpServletRequest request, HttpServletResponse response) throws Exception;@b@@b@}
2. ViewRendererServlet合成实现类
package org.springframework.web.servlet;@b@@b@import java.io.IOException;@b@import java.util.Map;@b@import javax.servlet.ServletException;@b@import javax.servlet.http.HttpServlet;@b@import javax.servlet.http.HttpServletRequest;@b@import javax.servlet.http.HttpServletResponse;@b@import org.springframework.web.util.NestedServletException;@b@@b@public class ViewRendererServlet extends HttpServlet@b@{@b@ public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE;@b@ public static final String VIEW_ATTRIBUTE = ViewRendererServlet.class.getName() + ".VIEW";@b@ public static final String MODEL_ATTRIBUTE = ViewRendererServlet.class.getName() + ".MODEL";@b@@b@ protected final void doGet(HttpServletRequest request, HttpServletResponse response)@b@ throws ServletException, IOException@b@ {@b@ processRequest(request, response);@b@ }@b@@b@ protected final void doPost(HttpServletRequest request, HttpServletResponse response)@b@ throws ServletException, IOException@b@ {@b@ processRequest(request, response);@b@ }@b@@b@ protected final void processRequest(HttpServletRequest request, HttpServletResponse response)@b@ throws ServletException, IOException@b@ {@b@ try@b@ {@b@ renderView(request, response);@b@ }@b@ catch (ServletException ex) {@b@ throw ex;@b@ }@b@ catch (IOException ex) {@b@ throw ex;@b@ }@b@ catch (Exception ex) {@b@ throw new NestedServletException("View rendering failed", ex);@b@ }@b@ }@b@@b@ protected void renderView(HttpServletRequest request, HttpServletResponse response)@b@ throws Exception@b@ {@b@ View view = (View)request.getAttribute(VIEW_ATTRIBUTE);@b@ if (view == null)@b@ throw new ServletException("Could not complete render request: View is null");@b@@b@ Map model = (Map)request.getAttribute(MODEL_ATTRIBUTE);@b@ view.render(model, request, response);@b@ }@b@}