View接口 1 2 3 4 5 6 7 8 9 10 11 12 13 public interface View { String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus" ; String PATH_VARIABLES = View.class.getName() + ".pathVariables" ; String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType" ; String getContentType () ; void render (Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception ; }
主要包含两个方法:
getContentType(): 获取view的内容类型。可以在渲染视图之前检查类型。
render(): 根据给定的模型渲染视图。
AbstractView类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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 ); 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); } if (this .requestContextAttribute != null ) { mergedModel.put(this .requestContextAttribute, createRequestContext(request, response, mergedModel)); } return mergedModel; }
createMergedOutputModel方法是用来将model和staticAttributes、pathVariables进行合并的。返回的mergeModel就是三者合并后的model。如果有冲突,优先级为model > pathVariables > staticAttributes,其中pathVariables可以通过设置 exposePathVariables 属性来关闭。
1 2 3 4 5 6 protected void prepareResponse (HttpServletRequest request, HttpServletResponse response) { if (generatesDownloadContent()) { response.setHeader("Pragma" , "private" ); response.setHeader("Cache-Control" , "private, must-revalidate" ); } }
prepareResponse主要是用来解决当通过https在IE浏览器进行下载时产生的bug。
1 2 protected abstract void renderMergedOutputModel ( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exceptions ;
renderMergedOutputModel是一个抽象方法。根据注释可以知道,子类必须实现这个方法来进行渲染视图。
那么最后render方法如下:
1 2 3 4 5 6 7 8 9 10 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, request, response); }
render会分别执行合并model,准备响应和渲染合并的model操作。那么我们剩下需要关注的就是不同view对于renderMergedOutputModel方法的实现。
接下来,此处我们看json的view类实现。
MappingJackson2JsonView类 此处我们直接看该类中的renderMergedOutputModel
1 2 3 4 5 6 7 8 9 10 protected void renderMergedOutputModel (Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { OutputStream stream = (this .updateContentLength ? createTemporaryOutputStream() : response.getOutputStream()); Object value = filterModel(model); writeContent(stream, value, this .jsonPrefix); if (this .updateContentLength) { writeToResponse(response, (ByteArrayOutputStream) stream); } }
此处可以看出该类主要的渲染方式,首先进行model过滤,然后将value写到stream中。
接下来看过滤操作,
1 2 3 4 5 6 7 8 9 10 protected Object filterModel (Map<String, Object> model) { Map<String, Object> result = new HashMap<String, Object>(model.size()); Set<String> renderedAttributes = (!CollectionUtils.isEmpty(this .modelKeys) ? this .modelKeys : model.keySet()); for (Map.Entry<String, Object> entry : model.entrySet()) { if (!(entry.getValue() instanceof BindingResult) && renderedAttributes.contains(entry.getKey())) { result.put(entry.getKey(), entry.getValue()); } } return (this .extractValueFromSingleKeyModel && result.size() == 1 ? result.values().iterator().next() : result); }
从源码中,我们可以看出,我们是可以自定义modelKeys的,若是未定义,则全部显示。filterModel会过滤掉model中所有BindingResult的值。网上查了一下,BindingResult好像是用来做验证错误返回的。
在这段代码中我们还可以看到一个有趣的属性extractValueFromSingleKeyModel,这个属性的作用就是当设置为true时,可以将对象直接序列化成不带变量名的json。举个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 { "result" : { "code" : 1 , "message" : "success" } } { "code" : 1 , "message" : "success" }
关于writeContent方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected void writeContent (OutputStream stream, Object value, String jsonPrefix) throws IOException { @SuppressWarnings("deprecation") JsonGenerator generator = this .objectMapper.getJsonFactory().createJsonGenerator(stream, this .encoding); if (this .objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) { generator.useDefaultPrettyPrinter(); } if (jsonPrefix != null ) { generator.writeRaw(jsonPrefix); } this .objectMapper.writeValue(generator, value); }
springMVC中调用了jackson库来处理。
类图
总结 json的视图渲染主要为如下步骤:
将model与静态属性进行合并,成新的model
对合并后的model中的属性进行过滤操作
调用jackson库进行渲染操作