目录
一、ModelAndView返回值的解析过程
1、适配返回值解析器并解析(handleReturnValue)- ModelAndViewMethodReturnValueHandler
2 、视图和数据处理(getModelAndView)
3、结果处理(processDispatchResult)
二、View为String类型的解析过程
1、适配返回值解析器并解析(handleReturnValue)- ViewNameMethodReturnValueHandler
2 、视图和数据处理(getModelAndView)
3、结果处理(processDispatchResult)
Thymeleaf。开始分析前先看看ModelAndView的结构:
public class ModelAndView {
// Object类型的视图,其实要不就是String类型,要不就是View类型
@Nullable
private Object view;
// ModelMap其实就是一个LinkedHashMap(需要注意是有序的)
@Nullable
private ModelMap model;
// Http状态,HttpServletResponse的状态
@Nullable
private HttpStatus status;
/** Indicates whether or not this instance has been cleared with a call to {@link #clear()}. */
private boolean cleared = false;
}
一、ModelAndView返回值的解析过程
1、适配返回值解析器并解析(handleReturnValue)- ModelAndViewMethodReturnValueHandler
继续在调用完Controller方法后,对结果进行适配:
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
ModelAndViewMethodReturnValueHandler类型。适配方法如下:
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}
直接用ModelAndView类型进行判断,其实还是比较直接。适配完直接,调用方法进行解析:
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
mavContainer.setRequestHandled(true);
return;
}
ModelAndView mav = (ModelAndView) returnValue;
if (mav.isReference()) {
String viewName = mav.getViewName();
mavContainer.setViewName(viewName);
if (viewName != null && isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else {
View view = mav.getView();
mavContainer.setView(view);
if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
mavContainer.setStatus(mav.getStatus());
mavContainer.addAllAttributes(mav.getModel());
}
直接将结果转换为ModelAndView,设置属性值到ModelAndViewContainer 中,getModelAndView中进行解析
1、视图可以是String或者View类型,所以分析进行处理。可能还需要处理重定向的属性
2、设置Http状态值
3、返回数据(设置MVC中的Model数据)
2 、视图和数据处理(getModelAndView)
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
上一步将解析的数据都设置到ModelAndViewContainer 中了,所以,当前只要对其进行处理即可。还是那些数据,只是重新new了一个ModelAndView,设置属性。 如果是重定向则将Spring MVC的九大件之一的FlashMap中的重定向属性进行了设置。
3、结果处理(processDispatchResult)
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
render方法进行渲染视图和模型,后续完成了拦截器(HandlerInteceptor)的后置处理方法回调。
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("省略");
}
} else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("省略");
}
}
// 省略try catch代码
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
1、获取local信息(国际化)
2、判断视图类型(如果是String类型需要进行解析具体的View)
3、View的render方法调用(当前为ThymeleafView的render,其他视图类型会有相应的渲染器,这里就不分析了)
其中,getViewName其实就是判断视图是String类型还是View类型。
@Nullable
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
解析String类型为View,又是挨个进行适配(但是ThymeleafView不是通过ThymeleafViewResolver适配出来的,而是ContentNegotiatingViewResolver),过程如下:
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
二、View为String类型的解析过程
1、适配返回值解析器并解析(handleReturnValue)- ViewNameMethodReturnValueHandler
ViewNameMethodReturnValueHandler类型。适配方法如下:
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
适配的类型可以是void,或者String的父类CharSequence类型。适配完直接,调用方法进行解析:
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
} else if (returnValue != null) {
throw new UnsupportedOperationException("省略");
}
}
上面判断了void类型,现在只判断为String类型的,否则就直接抛异常。处理更简单,只管String类型的视图添加进去,没有ModelMap就完了。
2 、视图和数据处理(getModelAndView)
getModelAndView时,new了一个ModelAndView,只是视图为String类型,ModelMap为null。
3、结果处理(processDispatchResult)
后面就与上面ModelAndView处理相同。