说在前面
前期回顾
sharding-jdbc源码解析 更新完毕
spring源码解析 更新完毕
spring-mvc源码解析 更新完毕
spring-tx源码解析 更新完毕
spring-boot源码解析 更新完毕
rocketmq源码解析 更新完毕
dubbbo源码解析 更新完毕
netty源码解析 更新完毕
spring源码架构更新完毕
springmvc源码架构更新中
springboot源码架构计划中
github https://github.com/tianheframe
sharding-jdbc源码解析 更新完毕
rocketmq源码解析 更新完毕
seata 源码解析 更新完毕
dubbo 源码解析 更新完毕
netty 源码解析 更新完毕
源码解析
org.springframework.web.servlet.View 用于web交互的MVC视图。实现负责呈现内容和公开模型。单个视图公开多个模型属性。这个类和与之相关的MVC方法在Rod Johnson (Wrox, 2002)的专家一对一J2EE设计和开发的第12章中进行了讨论。视图实现可能差别很大。一个明显的实现是基于jsp的。其他实现可能是基于xsl的,或者使用HTML生成库。此接口旨在避免限制可能实现的范围。视图应该是bean。它们很可能被视图解析器实例化为bean。由于这个接口是无状态的,所以视图实现应该是线程安全的。
String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
包含响应状态代码的HttpServletRequest属性的名称。注意:并非所有视图实现都支持此属性。
String PATH_VARIABLES = View.class.getName() + ".pathVariables";
HttpServletRequest属性的名称,该属性包含带有路径变量的映射。映射由基于字符串的URI模板变量名作为键及其对应的基于对象的值组成——从URL的片段中提取并转换类型。注意:并非所有视图实现都支持此属性。
String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";
在内容协商期间选择的MediaType,它可能比视图配置的MediaType更具体。例如:“应用程序/盾。example-v1 + xml”和“应用程序/ * + xml”。
String getContentType();
如果预先确定,返回视图的内容类型。可用于提前检查视图的内容类型,即在实际呈现尝试之前。
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
呈现给定指定模型的视图。第一步是准备请求:在JSP中,这意味着将模型对象设置为请求属性。第二步是视图的实际呈现,例如通过RequestDispatcher包含JSP。
org.springframework.web.servlet.SmartView 提供关于视图的附加信息,如视图是否执行重定向。
boolean isRedirectView();
是否是重定向视图
org.springframework.web.servlet.view.AbstractView 视图实现的抽象基类。子类应该是javabean,以便作为spring管理的bean实例进行方便的配置。
提供对视图可用的静态属性的支持,并提供多种方法来指定它们。静态属性将与每个呈现操作的给定动态属性(控制器返回的模型)合并。扩展WebApplicationObjectSupport,这将对某些视图有帮助。子类只需要实现实际的呈现。
public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=ISO-8859-1";
默认contentType
private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 4096;
初始化输出数组大小
public void setExposeContextBeansAsAttributes(boolean exposeContextBeansAsAttributes) { this.exposeContextBeansAsAttributes = exposeContextBeansAsAttributes; }
设置是否让应用程序上下文中的所有Spring bean都可以作为请求属性进行访问,方法是在属性被访问后进行惰性检查。这将使所有此类bean在普通${…}表达式,以及JSTL的c:out值表达式。默认设置是“假”。打开此标志以透明地公开request属性名称空间中的所有Spring bean。注意:上下文bean将覆盖手动添加的任何相同名称的自定义请求或会话属性。然而,相同名称的模型属性(如显式地公开给这个视图)总是会覆盖上下文bean。
public void setExposedContextBeanNames(String... exposedContextBeanNames) { this.exposedContextBeanNames = new HashSet<String>(Arrays.asList(exposedContextBeanNames)); }
指定应该公开的上下文中bean的名称。如果这是非空的,只有指定的bean才有资格作为属性公开。如果您想在应用程序上下文中公开所有Spring bean,请打开“exposeContextBeansAsAttributes”标志,但不要为此属性列出特定的bean名称。
@Override 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); }
准备给定指定模型的视图,必要时将其与静态属性和RequestContext属性合并。将实际呈现委托给renderMergedOutputModel。这里是模板方法实现
org.springframework.web.servlet.view.AbstractView#createMergedOutputModel 创建包含动态值和静态属性的组合输出映射(从不为空)。动态值优先于静态属性。
protected Map<String, Object> createMergedOutputModel(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) { @SuppressWarnings("unchecked") Map<String, Object> pathVars = (this.exposePathVariables ? (Map<String, Object>) request.getAttribute(View.PATH_VARIABLES) : null); // Consolidate static and dynamic model attributes. int size = this.staticAttributes.size(); size += (model != null ? model.size() : 0); size += (pathVars != null ? pathVars.size() : 0); Map<String, Object> mergedModel = new LinkedHashMap<String, Object>(size); mergedModel.putAll(this.staticAttributes); if (pathVars != null) { mergedModel.putAll(pathVars); } if (model != null) { mergedModel.putAll(model); } // Expose RequestContext? if (this.requestContextAttribute != null) { mergedModel.put(this.requestContextAttribute, createRequestContext(request, response, mergedModel)); } return mergedModel; }
org.springframework.web.servlet.view.AbstractView#createRequestContext 创建一个RequestContext,在指定的属性名下公开。默认实现为给定的请求和模型创建一个标准的RequestContext实例。可在自定义实例的子类中重写。
protected RequestContext createRequestContext( HttpServletRequest request, HttpServletResponse response, Map<String, Object> model) { return new RequestContext(request, response, getServletContext(), model); }
org.springframework.web.servlet.view.AbstractView#prepareResponse 为呈现准备给定的响应。默认的实现在通过HTTPS发送下载内容时应用了IE bug的解决方案。
protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) { if (generatesDownloadContent()) { response.setHeader("Pragma", "private"); response.setHeader("Cache-Control", "private, must-revalidate"); } }
org.springframework.web.servlet.view.AbstractView#generatesDownloadContent 返回该视图是否生成下载内容(通常是PDF或Excel文件等二进制内容)。默认实现返回false。如果子类知道在客户端生成需要临时缓存的下载内容(通常是通过响应OutputStream),那么在这里鼓励它们返回true。
org.springframework.web.servlet.view.AbstractView#renderMergedOutputModel 子类必须实现这个方法来实际呈现视图。第一步是准备请求:在JSP中,这意味着将模型对象设置为请求属性。第二步是视图的实际呈现,例如通过RequestDispatcher包含JSP。
org.springframework.web.servlet.view.AbstractView#exposeModelAsRequestAttributes 将给定映射中的模型对象公开为请求属性。名称将取自模型映射。此方法适用于javax.servlet.RequestDispatcher可访问的所有资源。
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() + "'"); } } } }
protected ByteArrayOutputStream createTemporaryOutputStream() { return new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE); }
为这个视图创建一个临时输出流。这通常用作IE解决方案,用于在实际将内容写入HTTP响应之前设置临时流的内容长度标头。
protected void writeToResponse(HttpServletResponse response, ByteArrayOutputStream baos) throws IOException { // Write content type and also length (determined via byte array). response.setContentType(getContentType()); response.setContentLength(baos.size()); // Flush byte array to servlet output stream. ServletOutputStream out = response.getOutputStream(); baos.writeTo(out); out.flush(); }
将给定的临时输出流写入HTTP响应。
protected void setResponseContentType(HttpServletRequest request, HttpServletResponse response) { MediaType mediaType = (MediaType) request.getAttribute(View.SELECTED_CONTENT_TYPE); if (mediaType != null && mediaType.isConcrete()) { response.setContentType(mediaType.toString()); } else { response.setContentType(getContentType()); } }
设置响应到配置的内容类型的内容类型,除非视图。出现SELECTED_CONTENT_TYPE请求属性,并将其设置为具体的媒体类型。
org.springframework.web.servlet.view.AbstractUrlBasedView 基于url视图的抽象基类。提供以“URL”bean属性的形式保存视图包装的URL的一致方式。
private String url;
url
@Override public void afterPropertiesSet() throws Exception { if (isUrlRequired() && getUrl() == null) { throw new IllegalArgumentException("Property 'url' is required"); } }
校验url
org.springframework.web.servlet.view.RedirectView 重定向到绝对URL、上下文相对URL或当前请求相对URL的视图。URL可能是URI模板,在这种情况下,URI模板变量将被模型中可用的值替换。默认情况下,所有基本模型属性(或其集合)都作为HTTP查询参数公开(假设它们没有用作URI模板变量),但是可以通过覆盖isEligibleProperty(String, Object)方法来更改这种行为。这个视图的URL应该是一个HTTP重定向URL,即适合HttpServletResponse的sendRedirect方法,如果HTTP 1.0标志是打开的,或者通过发送一个HTTP 303代码(如果HTTP 1.0兼容标志是关闭的)来重定向。注意,虽然“contextRelative”标志的默认值为off,但您可能希望几乎总是将其设置为true。当标志关闭时,以“/”开头的url被认为是相对于web服务器根的,而当标志打开时,它们被认为是相对于web应用程序根的。由于大多数web应用程序永远不会知道或关心它们的上下文路径实际上是什么,因此最好将此标志设置为true,并提交相对于web应用程序根目录的路径。在Portlet环境中使用此重定向视图时请注意:确保您的控制器遵守Portlet sendRedirect约束。
public void setExposePathVariables(boolean exposePathVariables) { this.exposePathVariables = exposePathVariables; }
指定是否向模型中添加路径变量。路径变量通常通过@PathVariable注释绑定到URI模板变量。它们实际上是URI模板变量,类型转换应用于它们以派生类型化的对象值。视图中经常需要这些值来构造指向相同url和其他url的链接。添加到模型中的路径变量覆盖静态属性(参见setAttributes(Properties)),但不覆盖模型中已经存在的属性。默认情况下,此标志设置为true。具体的视图类型可以覆盖它。
public void setContextRelative(boolean contextRelative) { this.contextRelative = contextRelative; }
设置是否将以斜杠("/")开头的给定URL解释为相对于当前ServletContext,即相对于web应用程序根目录的URL。默认值是“false”:以斜杠开头的URL将被解释为绝对的,即按原样接受。如果“true”,在这种情况下,上下文路径将在URL之前。
public void setHttp10Compatible(boolean http10Compatible) { this.http10Compatible = http10Compatible; }
sendRedirect设置是否与HTTP 1.0客户端保持兼容。在默认实现中,这将在任何情况下强制执行HTTP状态码302,即委托给HttpServletResponse.sendRedirect。关闭此选项将发送HTTP状态代码303,这是HTTP 1.1客户端的正确代码,但HTTP 1.0客户端不能理解。许多HTTP 1.1客户端对待302就像对待303一样,没有任何区别。但是,有些客户端在POST请求后重定向时依赖于303;在这种情况下,关闭此标志。
public void setExposeModelAttributes(final boolean exposeModelAttributes) { this.exposeModelAttributes = exposeModelAttributes; }
设置说明文属性标志,它表示模型属性是否应该作为HTTP查询参数公开。默认值为true。
public void setExpandUriTemplateVariables(boolean expandUriTemplateVariables) { this.expandUriTemplateVariables = expandUriTemplateVariables; }
是否将重定向URL视为URI模板。如果重定向URL包含左花括号“{”和右花括号“}”,并且不希望它们被解释为URI变量,则将此标志设置为false。默认值为true。
public void setPropagateQueryParams(boolean propagateQueryParams) { this.propagateQueryParams = propagateQueryParams; }
当设置为true时,将追加当前URL的查询字符串,从而传播到重定向的URL。默认值为false。
@Override public boolean isRedirectView() { return true; }
返回“true”,指示此视图执行重定向。
@Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws IOException {// 创建url String targetUrl = createTargetUrl(model, request);// 更新url targetUrl = updateTargetUrl(targetUrl, model, request, response); FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request); if (!CollectionUtils.isEmpty(flashMap)) { UriComponents uriComponents = UriComponentsBuilder.fromUriString(targetUrl).build(); flashMap.setTargetRequestPath(uriComponents.getPath()); flashMap.addTargetRequestParams(uriComponents.getQueryParams()); FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request); if (flashMapManager == null) { throw new IllegalStateException("FlashMapManager not found despite output FlashMap having been set"); } flashMapManager.saveOutputFlashMap(flashMap, request, response); }// 重定向 sendRedirect(request, response, targetUrl, this.http10Compatible); }
将模型转换为请求参数并重定向到给定的URL。
org.springframework.web.servlet.view.RedirectView#createTargetUrl 首先检查重定向字符串是否是URI模板,然后使用给定的模型展开它,然后可选地添加简单类型模型属性作为查询字符串参数,从而创建目标URL。
protected final String createTargetUrl(Map<String, Object> model, HttpServletRequest request) throws UnsupportedEncodingException { // Prepare target URL. StringBuilder targetUrl = new StringBuilder(); if (this.contextRelative && getUrl().startsWith("/")) { // Do not apply context path to relative URLs. targetUrl.append(getContextPath(request)); } targetUrl.append(getUrl()); String enc = this.encodingScheme; if (enc == null) { enc = request.getCharacterEncoding(); } if (enc == null) { enc = WebUtils.DEFAULT_CHARACTER_ENCODING; } if (this.expandUriTemplateVariables && StringUtils.hasText(targetUrl)) { Map<String, String> variables = getCurrentRequestUriVariables(request); targetUrl = replaceUriTemplateVariables(targetUrl.toString(), model, variables, enc); } if (isPropagateQueryProperties()) { appendCurrentQueryParams(targetUrl, request); } if (this.exposeModelAttributes) { appendQueryProperties(targetUrl, model, enc); } return targetUrl.toString(); }
org.springframework.web.servlet.view.RedirectView#updateTargetUrl 找到注册的RequestDataValueProcessor(如果有的话),并允许它更新重定向目标URL。
protected String updateTargetUrl(String targetUrl, Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) { WebApplicationContext wac = getWebApplicationContext(); if (wac == null) { wac = RequestContextUtils.findWebApplicationContext(request, getServletContext()); } if (wac != null && wac.containsBean(RequestContextUtils.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME)) { RequestDataValueProcessor processor = wac.getBean( RequestContextUtils.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME, RequestDataValueProcessor.class); return processor.processUrl(request, targetUrl); } return targetUrl; }
org.springframework.web.servlet.view.RedirectView#sendRedirect 将重定向发送回HTTP客户机
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String targetUrl, boolean http10Compatible) throws IOException { String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl)); if (http10Compatible) { HttpStatus attributeStatusCode = (HttpStatus) request.getAttribute(View.RESPONSE_STATUS_ATTRIBUTE); if (this.statusCode != null) { response.setStatus(this.statusCode.value()); response.setHeader("Location", encodedURL); } else if (attributeStatusCode != null) { response.setStatus(attributeStatusCode.value()); response.setHeader("Location", encodedURL); } else { // Send status code 302 by default. 执行response.sendRedirect重定向 response.sendRedirect(encodedURL); } } else { HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl); response.setStatus(statusCode.value()); response.setHeader("Location", encodedURL); } }
org.springframework.web.servlet.view.document.AbstractPdfStamperView 抽象超类,用于对具有AcroForm的现有文档进行操作的PDF视图。特定于应用程序的视图类将扩展这个类以合并PDF表单和模型数据。这个视图实现使用Bruno Lowagie的iText API。已知可以使用原始的iText 2.1.7及其分支OpenPDF。我们强烈推荐OpenPDF,因为它是积极维护的,并且修复了不可信PDF内容的一个重要漏洞。
public AbstractPdfStamperView(){ setContentType("application/pdf"); }
创建AbstractPdfStamperView,设置contentType=application/pdf
@Override protected final void renderMergedOutputModel( Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { // IE workaround: write into byte array first.创建输出流 ByteArrayOutputStream baos = createTemporaryOutputStream(); PdfReader reader = readPdfResource(); PdfStamper stamper = new PdfStamper(reader, baos);// 模板方法 mergePdfDocument(model, stamper, request, response); stamper.close(); // Flush to HTTP response. writeToResponse(response, baos); }
渲染输出model
org.springframework.web.servlet.view.AbstractView#createTemporaryOutputStream 为这个视图创建一个临时输出流。这通常用作IE解决方案,用于在实际将内容写入HTTP响应之前设置临时流的内容长度标头。
protected ByteArrayOutputStream createTemporaryOutputStream() { return new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE); }
org.springframework.web.servlet.view.document.AbstractPdfStamperView#mergePdfDocument 子类必须实现此方法来将PDF表单与给定的模型数据合并。这是您可以在AcroForm上设置值的地方。在这一级可以做的一个例子是:请注意,传入的HTTP响应只用于设置cookie或其他HTTP报头。在此方法返回后,构建的PDF文档本身将自动写入响应。
protected abstract void mergePdfDocument(Map<String, Object> model, PdfStamper stamper, HttpServletRequest request, HttpServletResponse response) throws Exception;
org.springframework.web.servlet.view.AbstractView#writeToResponse 将给定的临时输出流写入HTTP响应。
protected void writeToResponse(HttpServletResponse response, ByteArrayOutputStream baos) throws IOException { // Write content type and also length (determined via byte array). response.setContentType(getContentType()); response.setContentLength(baos.size()); // Flush byte array to servlet output stream. ServletOutputStream out = response.getOutputStream(); baos.writeTo(out); out.flush(); }
org.springframework.web.servlet.view.InternalResourceView 同一web应用程序中JSP或其他资源的包装器。将模型对象公开为请求属性,并使用RequestDispatcher将请求转发到指定的资源URL。这个视图的URL应该指定web应用程序中的一个资源,该资源适合RequestDispatcher的转发或包含方法。
如果在已包含的请求或已提交的响应中操作,该视图将退回到包含而不是转发。这可以通过在呈现视图之前调用response. flushbuffer()(它将提交响应)来实现。
public void setAlwaysInclude(boolean alwaysInclude) { this.alwaysInclude = alwaysInclude; }
指定是否总是包含视图而不是转发视图。默认设置是“假”。打开此标志以强制使用Servlet include,即使可能使用转发。
public void setPreventDispatchLoop(boolean preventDispatchLoop) { this.preventDispatchLoop = preventDispatchLoop; }
设置是否显式防止分派回当前处理程序路径。默认设置是“假”。对于基于约定的视图,将其切换为“true”,其中将分派回当前处理程序路径是一个确定的错误。
@Override protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Expose the model object as request attributes.将模型对象公开为请求属性。 exposeModelAsRequestAttributes(model, request); // Expose helpers as request attributes, if any.将帮助程序公开为请求属性(如果有的话)。模板方法 exposeHelpers(request); // Determine the path for the request dispatcher.确定请求分配器的路径。 String dispatcherPath = prepareForRendering(request, response); // Obtain a RequestDispatcher for the target resource (typically a JSP). 获取请求转发器 RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!"); } // If already included or response already committed, perform include, else forward. 如果include请求就执行,否则就转发 if (useInclude(request, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); }// 执行include rd.include(request, response); } else { // Note: The forwarded resource is supposed to determine the content type itself.注意:转发的资源应该确定内容类型本身。 if (logger.isDebugEnabled()) { logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); }// 执行转发 rd.forward(request, response); } }
根据指定的模型呈现内部资源。这包括将模型设置为请求属性。这里是模板方法实现,如果inlude请求执行include请求,如果是forward执行请求转发
org.springframework.web.servlet.view.AbstractView#exposeModelAsRequestAttributes 将给定映射中的模型对象公开为请求属性。名称将取自模型映射。此方法适用于javax.servlet.RequestDispatcher可访问的所有资源。
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() + "'"); } } } }
org.springframework.web.servlet.view.InternalResourceView#exposeHelpers 公开每个呈现操作唯一的助手。这是必要的,这样不同的呈现操作就不会覆盖彼此的上下文等等。由renderMergedOutputModel(Map, HttpServletRequest, HttpServletResponse)调用。默认实现为空。可以重写此方法,以将自定义助手添加为请求属性。
protected void exposeHelpers(HttpServletRequest request) throws Exception { }
org.springframework.web.servlet.view.InternalResourceView#prepareForRendering 准备呈现,并确定转发(或包含)的请求分派器路径。此实现仅返回配置的URL。子类可以覆盖它以确定要呈现的资源,通常以不同的方式解释URL。
protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response) throws Exception { String path = getUrl(); if (this.preventDispatchLoop) { String uri = request.getRequestURI(); if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) { throw new ServletException("Circular view path [" + path + "]: would dispatch back " + "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " + "(Hint: This may be the result of an unspecified view, due to default view name generation.)"); } } return path; }
org.springframework.web.servlet.view.InternalResourceView#getRequestDispatcher 获取用于转发/包含的RequestDispatcher。默认实现只是调用HttpServletRequest.getRequestDispatcher(字符串)。可以在子类中重写。
protected RequestDispatcher getRequestDispatcher(HttpServletRequest request, String path) { return request.getRequestDispatcher(path); }
protected boolean useInclude(HttpServletRequest request, HttpServletResponse response) { return (this.alwaysInclude || WebUtils.isIncludeRequest(request) || response.isCommitted()); }
确定是使用RequestDispatcher的include方法还是转发方法。执行检查请求中是否找到包含URI属性,指示包含请求,以及响应是否已经提交。在这两种情况下,都将执行include,因为转发不再可能。
org.springframework.web.util.WebUtils#isIncludeRequest 确定给定的请求是否是包含请求,即不是来自外部的顶级HTTP请求。检查“javax.servlet.include”的存在。request_uri”请求属性。可以检查仅出现在包含请求中的任何请求属性。
public static boolean isIncludeRequest(ServletRequest request) { return (request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null); }
org.springframework.web.servlet.view.JstlView JSTL页面的InternalResourceView的专门化,即使用JSP标准标记库的JSP页面。使用Spring的区域设置和MessageSource公开特定于JSTL的请求属性,这些属性指定JSTL的格式化和消息标记的区域设置和资源包。
org.springframework.web.servlet.view.AbstractTemplateView 用于基于模板的视图技术(如Velocity和FreeMarker)的适配器基类,可以在模型中使用请求和会话属性,还可以为Spring的Velocity/FreeMarker宏库公开助手对象。JSP/JSTL和其他视图技术自动访问HttpServletRequest对象,从而访问当前用户的请求/会话属性。此外,它们还能够创建帮助对象并将其缓存为请求属性本身。
public void setExposeRequestAttributes(boolean exposeRequestAttributes) { this.exposeRequestAttributes = exposeRequestAttributes; }
设置是否应该在与模板合并之前将所有请求属性添加到模型中。默认设置是“假”。
public void setAllowRequestOverride(boolean allowRequestOverride) { this.allowRequestOverride = allowRequestOverride; }
设置是否允许HttpServletRequest属性覆盖(隐藏)控制器生成的同名模型属性。默认值是“false”,如果发现与模型属性同名的请求属性,将引发异常。
public void setExposeSessionAttributes(boolean exposeSessionAttributes) { this.exposeSessionAttributes = exposeSessionAttributes; }
设置是否应该在与模板合并之前将所有HttpSession属性添加到模型中。默认设置是“假”
public void setAllowSessionOverride(boolean allowSessionOverride) { this.allowSessionOverride = allowSessionOverride; }
设置是否允许HttpSession属性覆盖(隐藏)控制器生成的同名模型属性。默认值是“false”,如果发现与模型属性同名的会话属性,将引发异常。
public void setExposeSpringMacroHelpers(boolean exposeSpringMacroHelpers) { this.exposeSpringMacroHelpers = exposeSpringMacroHelpers; }
设置是否以“springMacroRequestContext”的名称公开一个RequestContext以供Spring的宏库使用。默认设置是“真实的”。目前需要为Spring的Velocity和FreeMarker默认宏。注意,对于使用HTML表单的模板,这不是必需的,除非您希望利用Spring helper宏。
@Override 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); }
渲染输出model,这里是模板方法实现,org.springframework.web.servlet.view.AbstractTemplateView#applyContentType 将“contentType”bean属性中指定的视图内容类型应用于给定的响应。只有在响应上没有设置内容类型的情况下,才应用视图的contentType。这允许处理程序预先覆盖默认的内容类型。
protected void applyContentType(HttpServletResponse response) { if (response.getContentType() == null) { response.setContentType(getContentType()); } }
org.springframework.web.servlet.view.AbstractTemplateView#renderMergedTemplateModel 子类必须实现这个方法来实际呈现视图。
protected abstract void renderMergedTemplateModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
org.springframework.web.servlet.view.freemarker.FreeMarkerView 使用FreeMarker模板引擎查看。
公开以下JavaBean属性:url:要包装的FreeMarker模板相对于FreeMarker模板上下文(目录)的位置。编码(可选,默认由FreeMarker配置决定):FreeMarker模板文件的编码,依赖于一个单独的FreeMarkerConfig对象,例如FreeMarkerConfigurer,可以在当前web应用程序上下文中使用任何bean名称进行访问。或者,您可以将FreeMarker配置对象设置为bean属性。注意:Spring对FreeMarker的支持要求FreeMarker 2.3或更高。
private String encoding;
encoding
@Override protected void initServletContext(ServletContext servletContext) throws BeansException { if (getConfiguration() != null) { this.taglibFactory = new TaglibFactory(servletContext); } else { FreeMarkerConfig config = autodetectConfiguration(); setConfiguration(config.getConfiguration()); this.taglibFactory = config.getTaglibFactory(); } GenericServlet servlet = new GenericServletAdapter(); try {// 初始化servlet servlet.init(new DelegatingServletConfig()); } catch (ServletException ex) { throw new BeanInitializationException("Initialization of GenericServlet adapter failed", ex); } this.servletContextHashModel = new ServletContextHashModel(servlet, getObjectWrapper()); }
在启动时调用。查找单个FreeMarkerConfig bean以查找此工厂的相关配置。检查是否可以找到默认语言环境的模板:如果没有找到特定于语言环境的模板,FreeMarker将检查非特定于语言环境的模板。
@Override protected void renderMergedTemplateModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { exposeHelpers(model, request); doRender(model, request, response); }
通过将模型映射与FreeMarker模板合并来处理它。输出被定向到servlet响应。如果需要自定义行为,可以重写此方法。
@Override protected void renderMergedTemplateModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { exposeHelpers(model, request);// 渲染模型 doRender(model, request, response); }
org.springframework.web.servlet.view.freemarker.FreeMarkerView#exposeHelpers 公开每个呈现操作唯一的助手。这是必要的,以便不同的呈现操作不会覆盖彼此的格式等。叫renderMergedTemplateModel。默认实现为空。可以重写此方法以向模型中添加自定义助手。
protected void exposeHelpers(Map<String, Object> model, HttpServletRequest request) throws Exception { }
org.springframework.web.servlet.view.freemarker.FreeMarkerView#doRender 使用包含要使用的完整模板模型的给定模型映射,将FreeMarker视图呈现给给定的响应。默认实现呈现由“url”bean属性指定的模板,该属性通过getTemplate检索。它委托processTemplate方法将模板实例与给定的模板模型合并。将标准的Freemarker散列模型添加到模型中:请求参数、请求、会话和应用程序(ServletContext),以及JSP标记库散列模型。可以重写以自定义行为,例如将多个模板呈现到单个视图中。
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); }
org.springframework.web.servlet.view.json.AbstractJackson2View 抽象基类,用于基于Jackson和内容类型独立的AbstractView实现。
private ObjectMapper objectMapper;
objectMapper
@Override protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) { setResponseContentType(request, response); response.setCharacterEncoding(this.encoding.getJavaName()); if (this.disableCaching) { response.addHeader("Cache-Control", "no-store"); } }
设置响应contentType、缓存相关header
@Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { OutputStream stream = (this.updateContentLength ? createTemporaryOutputStream() : response.getOutputStream()); Object value = filterAndWrapModel(model, request); writeContent(stream, value); if (this.updateContentLength) { writeToResponse(response, (ByteArrayOutputStream) stream); } }
渲染输出model
org.springframework.web.servlet.view.xml.MappingJackson2XmlView Spring MVC视图,通过使用Jackson 2的XmlMapper序列化当前请求的模型来呈现XML内容。要序列化的对象作为模型中的参数提供。使用第一个可序列化条目。用户可以通过sourceKey属性在模型中指定一个特定的条目。默认构造函数使用Jackson2ObjectMapperBuilder提供的默认配置。
public static final String DEFAULT_CONTENT_TYPE = "application/xml";
默认contentType
@Override protected Object filterModel(Map model) { Object value = null; if (this.modelKey != null) { value = model.get(this.modelKey); if (value == null) { throw new IllegalStateException( "Model contains no object with key [" + this.modelKey + "]"); } } else { for (Map.Entry entry : model.entrySet()) { if (!(entry.getValue() instanceof BindingResult) && !entry.getKey().equals(JsonView.class.getName())) { if (value != null) { throw new IllegalStateException("Model contains more than one object to render, only one is supported"); } value = entry.getValue(); } } } return value; }
解析json model
org.springframework.web.servlet.view.json.MappingJackson2JsonView Spring MVC视图,通过使用Jackson 2的ObjectMapper序列化当前请求的模型来呈现JSON内容。默认情况下,模型映射的整个内容(框架特定类除外)将被编码为JSON。如果模型只包含一个键,您可以通过setExtractValueFromSingleKeyModel将其提取编码为JSON。默认构造函数使用Jackson2ObjectMapperBuilder提供的默认配置。
public static final String DEFAULT_CONTENT_TYPE = "application/json";
默认内容类型:“application/json”。通过setContentType重写的。
public void setExtractValueFromSingleKeyModel(boolean extractValueFromSingleKeyModel) { this.extractValueFromSingleKeyModel = extractValueFromSingleKeyModel; }
设置是将包含单个属性的模型序列化为映射,还是从模型中提取单个值并直接序列化它。设置此标志的效果类似于使用带有@ResponseBody请求处理方法的MappingJackson2HttpMessageConverter。默认的是假的
org.springframework.web.servlet.view.xml.MarshallingView 允许响应上下文作为编组器编组的结果呈现的Spring-MVC视图。将编组的对象作为模型中的参数提供,然后在响应呈现期间检测。用户可以通过sourceKey属性在模型中指定一个特定的条目,或者让Spring定位源对象。
public static final String DEFAULT_CONTENT_TYPE = "application/xml";
默认contentType
private Marshaller marshaller;
marshaller
@Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { Object toBeMarshalled = locateToBeMarshalled(model); if (toBeMarshalled == null) { throw new IllegalStateException("Unable to locate object to be marshalled in model: " + model); } ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); this.marshaller.marshal(toBeMarshalled, new StreamResult(baos)); setResponseContentType(request, response); response.setContentLength(baos.size()); baos.writeTo(response.getOutputStream()); }
渲染输出model
org.springframework.web.servlet.view.document.AbstractXlsView 方便的超类,用于传统XLS格式的Excel文档视图。兼容Apache POI 3.5及更高版本。
public AbstractXlsView() { setContentType("application/vnd.ms-excel"); }
设置contentType=application/vnd.ms-excel
@Override protected final void renderMergedOutputModel( Map model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Create a fresh workbook instance for this render step. Workbook workbook = createWorkbook(model, request); // Delegate to application-provided document code.模板方法 buildExcelDocument(model, workbook, request, response); // Set the content type. response.setContentType(getContentType()); // Flush byte array to servlet output stream. renderWorkbook(workbook, response); }
渲染视图model,这里是模板方法实现,org.springframework.web.servlet.view.document.AbstractXlsView#buildExcelDocument 给定模型,应用程序提供的子类必须实现此方法来填充Excel工作簿文档。
protected abstract void buildExcelDocument( Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception;
org.springframework.web.servlet.view.document.AbstractXlsxView Office 2007 XLSX格式(由POI-OOXML支持)中Excel文档视图的方便超类。兼容Apache POI 3.5及更高版本。
public AbstractXlsxView() { setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); }
设置contentType=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
org.springframework.web.servlet.view.document.AbstractXlsxStreamingView Office 2007 XLSX格式的Excel文档视图,使用POI的流媒体变体,方便的超类。兼容Apache POI 3.9及更高版本。
org.springframework.web.servlet.view.document.AbstractPdfView PDF视图的抽象超类。特定于应用程序的视图类将扩展这个类。视图将保存在子类本身中,而不是在模板中。这个视图实现使用Bruno Lowagie的iText API。已知可以使用原始的iText 2.1.7及其分支OpenPDF。我们强烈推荐OpenPDF,因为它是积极维护的,并且修复了不可信PDF内容的一个重要漏洞。注:Internet Explorer需要一个"。扩展,因为它并不总是尊重声明的内容类型。
public AbstractPdfView() { setContentType("application/pdf"); }
此构造函数设置适当的内容类型“application/pdf”。请注意,IE不会注意到这一点,但我们对此无能为力。生成的文档应该有一个“”。pdf”扩展。
@Override protected final void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // IE workaround: write into byte array first.IE工作区:写入字节数组第一。 ByteArrayOutputStream baos = createTemporaryOutputStream(); // Apply preferences and build metadata. Document document = newDocument(); PdfWriter writer = newWriter(document, baos); prepareWriter(model, writer, request);// 模板方法 buildPdfMetadata(model, document, request); // Build PDF document. document.open();// 模板方法 buildPdfDocument(model, document, writer, request, response); document.close(); // Flush to HTTP response. writeToResponse(response, baos); }
渲染输出model,这里是模板方法实现。org.springframework.web.servlet.view.document.AbstractPdfView#buildPdfMetadata 填充iText文档的元字段(作者、标题等)。默认值是一个空实现。子类可以重写此方法来添加元字段,如标题、主题、作者、创建者、关键字等。此方法在将PdfWriter分配给文档后调用,并在调用Document .open()之前调用。
protected void buildPdfMetadata(Map<String, Object> model, Document document, HttpServletRequest request) { }
org.springframework.web.servlet.view.document.AbstractPdfView#buildPdfDocument 子类必须实现此方法来构建给定模型的iText PDF文档。在Document.open()和Document.close()调用之间调用。请注意,传入的HTTP响应只用于设置cookie或其他HTTP报头。在此方法返回后,构建的PDF文档本身将自动写入响应。
protected abstract void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer, HttpServletRequest request, HttpServletResponse response) throws Exception;
org.springframework.web.servlet.view.AbstractView#writeToResponse 将给定的临时输出流写入HTTP响应。
protected void writeToResponse(HttpServletResponse response, ByteArrayOutputStream baos) throws IOException { // Write content type and also length (determined via byte array). response.setContentType(getContentType()); response.setContentLength(baos.size()); // Flush byte array to servlet output stream. ServletOutputStream out = response.getOutputStream(); baos.writeTo(out); out.flush(); }
说在最后
本次解析仅代表个人观点,仅供参考。
扫码进入技术微信群
钉钉技术群qq技术群