目录
  1. View接口
  2. AbstractView类
  3. MappingJackson2JsonView类
  4. 类图
  5. 总结
springMVC(9) View实现之MappingJackson2JsonView

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

// 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 属性来关闭。

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
//false
{
"result": {
"code": 1,
"message": "success"
}
}

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

类图

总结

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

  1. 将model与静态属性进行合并,成新的model
  2. 对合并后的model中的属性进行过滤操作
  3. 调用jackson库进行渲染操作
文章作者: 谷河
文章链接: https://www.lyytaw.com/spring/springMVC(9)-View%E5%AE%9E%E7%8E%B0%E4%B9%8BMappingJackson2JsonView/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 谷河|BLOG