SpringMVC源码学习(六)---解析ModelAndView
作者:互联网
解析ModelAndView
- 一. RequestMappingHandlerAdapter类invokeHandlerMethod()方法
- 二. invokeAndHandle() 方法
- 三. handleReturnValue()方法
- 四. ModelAndViewMethodReturnValueHandler类中的handleReturnValue() 方法
- 五. RequestMappingHandlerAdapter类中的getModelAndView()方法
- 六. DispatcherServlet类中的processDispatchResult()方法
- 七. DispatcherServlet类中的processHandlerException()方法
- 八. DispatcherServlet类中的render()方法
- 九. ViewResolver和View接口
- 十. DispatcherServlet类中的resolveViewName()方法
- 十一. AbstractCachingViewResolver类中的resolveViewName()方法
- 十二. UrlBasedViewResolver类中的createView()方法
- 十三. UrlBasedViewResolver类中的loadView()方法
- 十四. UrlBasedViewResolver类中的buildView()方法
- 十五. UrlBasedViewResolver类中的applyLifecycleMethods()方法
- 十六. AbstractView类中的render()方法
- 十七. InternalResourceView类中的renderMergedOutputModel()方法
一. RequestMappingHandlerAdapter类invokeHandlerMethod()方法
在RequestMappingHandlerAdapter#invokeHandlerMethod()方法中
- 执行处理器方法(HandlerMethod)
- 封装并返回ModelAndView实例
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//将Request和Response进行封装
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//WebDataBinderFactory --> 工厂类,为目标对象创建一个WebDataBinder实例
// 1.WebDataBinder继承了DataBinder类,为web请求提供了参数绑定服务
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取ModelFactory:
// 2.ModelFactory可以协助控制器在调用方法之前初始化模型,并在调用之后更新模型
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 创建InvocableHandlerMethod实例,以及各个组件的配置;
// 后面通过调用invokeAndHandle()方法执行处理器
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 4.尝试绑定参数、返回值解析器
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 创建视图容器, 用于封装视图, 数据模型, 处理状态等信息
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 6.异步请求相关
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 7.调用Controller中的具体方法并处理返回值 执行处理器
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 8.返回ModelAndView对象
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
// 完成请求后续处理,并将当前请求置为未激活
webRequest.requestCompleted();
}
}
二. invokeAndHandle() 方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 1.调用Controller中的具体方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 2.设置返回状态码
setResponseStatus(webRequest);
// 3.当前请求无返回值或者返回值中包含错误,则将请求完成标识设置为true并返回
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// 4.当前请求有返回值且无错误信息,则将请求完成标识设置为false,并继续处理当前请求
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 选取合适的HandlerMethodReturnValueHandler,并处理返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
三. handleReturnValue()方法
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//根据返回值以及返回类型选择合适的返回值处理器, 对返回值进行解析
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
//解析结果
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
四. ModelAndViewMethodReturnValueHandler类中的handleReturnValue() 方法
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 当前请求返回值为null,无需处理,并且要将当前请求标记已处理
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);
}
}
// 处理普通视图(即我们已经制定了具体的View视图,而无需通过视图解析器再次解析)
else {
View view = mav.getView();
mavContainer.setView(view);
if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
// 设置返回状态
mavContainer.setStatus(mav.getStatus());
//设置数据Model
mavContainer.addAllAttributes(mav.getModel());
}
五. RequestMappingHandlerAdapter类中的getModelAndView()方法
从ModelAndViewContainer容器中获取ModeAndView实例
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
// 1.更新模型
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
// 2.获取ModelMap并创建ModelAndView
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
// 3.处理引用类型视图和转发类型视图
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;
}
六. DispatcherServlet类中的processDispatchResult()方法
解析ModelAndView
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);
}
}
// 尝试解析视图和模型;
// wasCleared:判断当前模型和视图是否已经被标识为清空,且当前视图和模型是否同时为空
if (mv != null && !mv.wasCleared()) {
// 解析并渲染视图
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
// 处理注册的后置完成拦截器
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
processDispatchResult() 方法
- 如果出现异常,则解析异常视图
- 解析ModelAndView
七. DispatcherServlet类中的processHandlerException()方法
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
//遍历所有的异常解析器, 尝试对异常进行解析, 如果解析成功,跳出循焕
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
//对于简单的错误模型,我们可能仍需要视图名称转换
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
//设置错误请求的相关属性
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
八. DispatcherServlet类中的render()方法
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 确定请求的区域设置并将其应用于响应。
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
// 获取视图名
String viewName = mv.getViewName();
if (viewName != null) {
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
// 获取到视图名,再次判断当前ModelAndView对象中是否包含真正的View对象,
// 因为接下来需要调用View对象的render方法
else {
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
// 设置返回状态码
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 调用View对象的render方法完成视图解析
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
ModelAndView视图解析可以分为两个重要步骤:
- 根据视图名称创建View实例
- 根据View实例渲染视图
九. ViewResolver和View接口
public interface ViewResolver {
// 通过逻辑视图名和用户地区信息生成View对象
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
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";
//获取返回值的contentType
@Nullable
default String getContentType() {
return null;
}
//通过用户提供的模型数据与视图信息渲染视图
void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
十. DispatcherServlet类中的resolveViewName()方法
//根据视图名称创建View实例
@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;
}
十一. AbstractCachingViewResolver类中的resolveViewName()方法
public View resolveViewName(String viewName, Locale locale) throws Exception {
//判断缓存是否可用
if (!isCache()) {
//如果缓存不可用, 则直接创建视图
return createView(viewName, locale);
}
else {
//如果缓存可用, 则先尝试从缓存中获取
//生成缓存Key
Object cacheKey = getCacheKey(viewName, locale);
//尝试从缓存中获取视图
View view = this.viewAccessCache.get(cacheKey);
if (view == null) {
//如果从缓存中获取视图失败, 则尝试从viewCreationCache缓存中获取
synchronized (this.viewCreationCache) {
//让子类创建View对象, 留给子类扩展[扩展开放,修改关闭原则]
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// 这里cacheUnresolved指的是是否缓存默认的空视图,UNRESOLVED_VIEW是
// 一个没有任何内容的View
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null) {
//将创建的view视图加入缓存
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
十二. UrlBasedViewResolver类中的createView()方法
@Override
protected View createView(String viewName, Locale locale) throws Exception {
if (!canHandle(viewName, locale)) {
return null;
}
// 检查特殊的"redirect:"前缀 REDIRECT_URL_PREFIX = "redirect:"
// 如果是以"redirect:" 开头, 说明该视图是重定向
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl,
isRedirectContextRelative(), isRedirectHttp10Compatible());
String[] hosts = getRedirectHosts();
if (hosts != null) {
view.setHosts(hosts);
}
return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
}
// 检查特殊的"forward:"前缀 FORWARD_URL_PREFIX = "forward:"
// 如果是以"forward:" 开头, 说明该视图是请求转发
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
//如果是普通视图, 创建该视图视图
return super.createView(viewName, locale);
}
十三. UrlBasedViewResolver类中的loadView()方法
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
// 使用逻辑视图名按照指定规则生成View对象
AbstractUrlBasedView view = buildView(viewName);
// 应用声明周期函数,也就是调用View对象的初始化函数和Spring用于切入bean创建的
View result = applyLifecycleMethods(viewName, view);
// 检查view的准确性,这里默认始终返回true
return (view.checkResource(locale) ? result : null);
}
十四. UrlBasedViewResolver类中的buildView()方法
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//对于InternalResourceViewResolver而言,其返回的View对象的具体类型是InternalResourceView
Class<?> viewClass = getViewClass();
Assert.state(viewClass != null, "No view class");
// 使用反射生成InternalResourceView对象实例
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
//根据前缀和后缀拼接视图路径信息
view.setUrl(getPrefix() + viewName + getSuffix());
// 设置View的contentType属性
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
// 设置contextAttribute和attributeMap等属性
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
// pathVariables表示request请求url中的属性,这里主要是设置是否将这些属性暴露到视图中
Boolean exposePathVariables = getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
// 这里设置的是是否将Spring的bean暴露在视图中,以供给前端调用
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
if (exposeContextBeansAsAttributes != null) {
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
}
// 设置需要暴露给前端页面的bean名称
String[] exposedContextBeanNames = getExposedContextBeanNames();
if (exposedContextBeanNames != null) {
view.setExposedContextBeanNames(exposedContextBeanNames);
}
return view;
}
十五. UrlBasedViewResolver类中的applyLifecycleMethods()方法
十六. AbstractView类中的render()方法
protected View applyLifecycleMethods(String viewName, AbstractUrlBasedView view) {
ApplicationContext context = getApplicationContext();
if (context != null) {
// 对生成的View对象应用初始化方法,主要包括InitializingBean.afterProperties()和一些
// Processor,Aware方法
Object initialized = context.getAutowireCapableBeanFactory().initializeBean(view, viewName);
if (initialized instanceof View) {
return (View) initialized;
}
}
return view;
}
@Override
public void render(@Nullable 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对象,以供给后面对视图的渲染使用
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
// 判断当前View对象的类型是否为文件下载类型,如果是文件下载类型,则设置response的
prepareResponse(request, response);
// 开始view视图渲染以及数据输出整理
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
十七. InternalResourceView类中的renderMergedOutputModel()方法
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 将Model中的键值对数据全部写进RequestScope中
exposeModelAsRequestAttributes(model, request);
// 提供的一个hook方法,默认是空实现,用于用户进行request属性的自定义使用
exposeHelpers(request);
//确定请求分配器的路径
String dispatcherPath = prepareForRendering(request, response);
// 获取可应用于 forward/include 的RequestDispatcher
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// 判断当前是否为include请求,如果是,则调用RequestDispatcher.include()方法进行文件引入
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(request, response);
}
// 请求转发
//使用forward跳转则后面的response输出则不会执行,而用include来跳转,
//则include的servlet执行完后,再返回到原来的servlet执行response的输出(如果有)
else {
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
//如果当前不是include()请求,则直接使用forward请求将当前请求转发到目标文件路径中,从而渲染该视图
rd.forward(request, response);
}
}
视图解析分析完成
标签:SpringMVC,request,mavContainer,视图,---,源码,View,null,view 来源: https://blog.csdn.net/weixin_45480785/article/details/116596262