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库来处理。
类图
总结
json的视图渲染主要为如下步骤:
- 将model与静态属性进行合并,成新的model
- 对合并后的model中的属性进行过滤操作
- 调用jackson库进行渲染操作