美文网首页
springMVC(9) View实现之MappingJacks

springMVC(9) View实现之MappingJacks

作者: 谷和阿秋 | 来源:发表于2018-01-12 21:23 被阅读0次

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

    主要包含两个方法:

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

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

    AbstractView类

    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;
    }
    

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

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

    那么最后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);
       }
    
       Map<String, Object> 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<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中。

    接下来看过滤操作,

    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。举个例子:

    //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库来处理。

    类图

    MappingJackson2JsonView类图

    总结

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

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

    相关文章

      网友评论

          本文标题:springMVC(9) View实现之MappingJacks

          本文链接:https://www.haomeiwen.com/subject/pnkqoxtx.html