springMVC(9) View实现之MappingJackson2JsonView

View接口

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 model, HttpServletRequest request, HttpServletResponse response) throws Exception;

}

主要包含两个方法:

getContentType(): 获取view的内容类型。可以在渲染视图之前检查类型。

render(): 根据给定的模型渲染视图。

AbstractView类

protected Map createMergedOutputModel(Map model, HttpServletRequest request,
      HttpServletResponse response) {

   @SuppressWarnings("unchecked")
   Map pathVars = (this.exposePathVariables ?
      (Map) 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 mergedModel = new LinkedHashMap(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;
}

createMergedOutputModel方法是用来将model和staticAttributes、pathVariables进行合并的。返回的mergeModel就是三者合并后的model。如果有冲突,优先级为model > pathVariables > staticAttributes,其中pathVariables可以通过设置 exposePathVariables 属性来关闭。

protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {
   if (generatesDownloadContent()) {
      response.setHeader("Pragma", "private");
      response.setHeader("Cache-Control", "private, must-revalidate");
   }
}

prepareResponse主要是用来解决当通过https在IE浏览器进行下载时产生的bug。

protected abstract void renderMergedOutputModel(
      Map model, HttpServletRequest request, HttpServletResponse response) throws Exceptions;

renderMergedOutputModel是一个抽象方法。根据注释可以知道,子类必须实现这个方法来进行渲染视图。

那么最后render方法如下:

public void render(Map 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 mergedModel = createMergedOutputModel(model, request, response);
   prepareResponse(request, response);
   renderMergedOutputModel(mergedModel, request, response);
}

render会分别执行合并model,准备响应和渲染合并的model操作。那么我们剩下需要关注的就是不同view对于renderMergedOutputModel方法的实现。

接下来,此处我们看json的view类实现。

MappingJackson2JsonView类

此处我们直接看该类中的renderMergedOutputModel

protected void renderMergedOutputModel(Map 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中。

接下来看过滤操作,

protected Object filterModel(Map model) {
   Map result = new HashMap(model.size());
   Set renderedAttributes = (!CollectionUtils.isEmpty(this.modelKeys) ? this.modelKeys : model.keySet());
   for (Map.Entry 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。举个例子:

//false
{
  "result": {
    "code": 1,
    "message": "success"
  }
}

//true
{
  "code": 1,
  "message": "success"
}

关于writeContent方法,

protected void writeContent(OutputStream stream, Object value, String jsonPrefix) throws IOException {
   // The following has been deprecated as late as Jackson 2.2 (April 2013);
   // preserved for the time being, for Jackson 2.0/2.1 compatibility.
   @SuppressWarnings("deprecation")
   JsonGenerator generator = this.objectMapper.getJsonFactory().createJsonGenerator(stream, this.encoding);

   // A workaround for JsonGenerators not applying serialization features
   // https://github.com/FasterXML/jackson-databind/issues/12
   if (this.objectMapper.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
      generator.useDefaultPrettyPrinter();
   }

   if (jsonPrefix != null) {
      generator.writeRaw(jsonPrefix);
   }
   this.objectMapper.writeValue(generator, value);
}

springMVC中调用了jackson库来处理。

类图

springMVC(9) View实现之MappingJackson2JsonView_第1张图片
MappingJackson2JsonView类图

总结

json的视图渲染主要为如下步骤:

  1. 将model与静态属性进行合并,成新的model
  2. 对合并后的model中的属性进行过滤操作
  3. 调用jackson库进行渲染操作

你可能感兴趣的:(springMVC(9) View实现之MappingJackson2JsonView)