首先,我们还是从 DispatcherServlet .doDispatch( HttpServletRequest request, HttpServletResponse response) throws Exception方法开始,看看ModelAndView是怎么开始的,又是怎么结束的:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.调用handler方法,返回ModelAndView类型的对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 设置mv的Object view属性值,是一个String类型(依据request的URI计算,加上一个前缀和后缀得到)
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
从上面的源代码可以看出,mv是在调用handler方法的时候返回的(即便,我们@RequestMapping注解标识的handler方法很多时候返回的是一个String,或者是View类型。但是,反射是无所不能的,你懂的...)。我们知道,@RequestMapping标注的handler方法通常能够返回三种类型的结果:String,ModelAndView和View。从上面的ha.handle方法我们知道,动态代理最后都会将结果转化成ModelAndView类型。
一、得到mv
下面对 mv = ha.handle(processedRequest, response, mappedHandler.getHandler())方法中如何得到mv实例的演变过程进行分析:
从 SpringMVC源码分析 中分析ha.handle方法的流程时我们知道:
①、HandlerAdapter是一个接口类型;
②、AbstractHandlerMethodAdapter是一个抽象类,该抽象类实现了HandlerAdapter接口;
③、AbstractHandlerMethodAdapter的handle是一个public final类型的具体方法,此方法直接调用了AbstractHandlerMethodAdapter中protected abstract类型的抽象方法handleInternal(该方法由具体的子类来实现);
④、 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler())语句会从 mappedHandler 队列中找到第一个能够处理该请求的 HandlerAdapter 的具体实现的一个实例,将其向上转型为 HandlerAdapter 并且赋值给ha;
⑤、@RequestMapping标注的handler方法都由RequestMappingHandlerAdapter来处理,这个Adapter继承自抽象类AbstractHandlerMethodAdapter。所以,AbstractHandlerMethodAdapter.handleInternal方法由 RequestMappingHandlerAdapter . handleInternal 来实现;
⑥、总结上面①~⑤的分析,我们可以得出这样一个简单的结论:doDispatch中的ha.handle方法实际上是调用了 RequestMappingHandlerAdapter. handleInternal 方法;
有了以上⑥的结论以后,我们继续追踪ModelAndView的源头,从 RequestMappingHandlerAdapter. handleInternal 追踪到 RequestMappingHandlerAdapter .invokeHandleMethod,我们来看一看 invokeHandleMethod 的源代码:
/**
* Invoke the RequestMapping handler method preparing a ModelAndView if view resolution is required.
*/
private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
//step 1、ModelAndView的前世:是一个ModelAndViewContainer实例
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
/*
* RequestContextUtils.getInputFlashMap(request)可以获取到request中的attribute,
* 并且将所有的request中的attribute放置在mavContainer中,此时使用的是defaultModel
*/
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
/*
* 在RequestMappingHandlerAdapter中ignoreDefaultModelOnRedirect默认为false
*/
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
//省略许多代码...
if (asyncManager.hasConcurrentResult()) {
//...
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
//...
}
//step 2、调用handler方法,
requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
//省略许多代码...
//step 3、从mavContainer中获取到ModelAndView实例,返回
return getModelAndView(mavContainer, modelFactory, webRequest);
}
看看上面代码中ModelAndViewContainer是个什么东西?同样从SpringMVC源码分析中我们可以得出如下的结论:
①、 ModelMap实际上就是一个LinkedHashMap
②、 ModelAndViewContainer含有两个ModelMap对象 : defaultModel和redirectModel,默认情况下使用defaultModel ;
有了以上两个结论,我们还有必要分析一下ModelAndViewContainer的一些细节(主要参看下面代码中的注释):
public class ModelAndViewContainer {
/*
* 对这7个属性我们可以将其分为3个部分来看
*/
// 1、两个模型和一个view,其中view可以存放任何java类型
private Object view;
// * 注意这个是final类型,也就是不能再重新为defaultModel赋值
private final ModelMap defaultModel = new BindingAwareModelMap();
private ModelMap redirectModel;
// 2、记录当前的Model使用策略
private boolean ignoreDefaultModelOnRedirect = false;
private boolean redirectModelScenario = false;
// 3、记录request和session的处理状态
private final SessionStatus sessionStatus = new SimpleSessionStatus();
private boolean requestHandled = false;
//和view属性相关的处理方法
/*
* 对view的处理主要分为String类型和非String类型。如果views是一个
* String类型的对象,那么它就是下面说的isViewReference
*/
public void setViewName(String viewName) {
this.view = viewName;
}
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
public void setView(Object view) {
this.view = view;
}
public Object getView() {
return this.view;
}
/*
* 注意,如果veiw是一个String类型,那么isViewReference
*/
public boolean isViewReference() {
return (this.view instanceof String);
}
//和model相关的处理方法
/*
* 假设redirectModelScenario = R ,ignoreDefaultModelOnRedirect = I ,(redirectModel == null)= M
* 那么(R, I, M)共有8中组合情况,useDefaultModel返回false(也就是使用redirectModel)只有三种情况:
* (1,1,0)、(1,1,1)、(1,0,0)
* a:如果同时设置了redirectModelScenario和ignoreDefaultModelOnRedirect为true,那么无论redirectModel
* 是否为null,都会使用redirectModel;
* b:如果设置了redirectModelScenario为true,而ignoreDefaultModelOnRedirect为false,同时redirectModel
* 为null,那么也会使用redirectModel;
*/
private boolean useDefaultModel() {
return (!this.redirectModelScenario || (!this.ignoreDefaultModelOnRedirect && this.redirectModel == null));
}
/*
* 这个方法是重要的,通过设置redirectModelScenario和ignoreDefaultModelOnRedirect来影响该方法的返回值;
* 如果没有初始化redirectModel,那么就会new一个ModelMap对象进行返回;
*/
public ModelMap getModel() {
if (useDefaultModel()) {
return this.defaultModel;
}
else {
return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
}
}
public ModelMap getDefaultModel() {
return this.defaultModel;
}
public void setRedirectModel(ModelMap redirectModel) {
this.redirectModel = redirectModel;
}
/**
* Whether the controller has returned a redirect instruction, e.g. a
* "redirect:" prefixed view name, a RedirectView instance, etc.
*/
public void setRedirectModelScenario(boolean redirectModelScenario) {
this.redirectModelScenario = redirectModelScenario;
}
public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
}
//省略一些方法...
}
看看 requestMappingMethod.invokeAndHandle(webRequest, mavContainer); 中对mavContainer做了什么事情?看看 requestMappingMethod.invokeAndHandle(webRequest, mavContainer) 的源代码:
/**
*Invokes the method and handles the return value through one of the configured HandlerMethodReturnValueHandlers.
*/
public void invokeAndHandle(ServletWebRequest webRequest,
ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 调用handler方法,的到返回结果为returnValue
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {//handler返回null
if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}else if (StringUtils.hasText(this.responseReason)) {
mavContainer.setRequestHandled(true);
return;
}
// 设置标志位,表示当前request请求还没有处理完成
mavContainer.setRequestHandled(false);
try {
// 处理返回结果returnValue
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;
}
}
看看上面代码中是如何处理返回结果returnValue的, this .returnValueHandlers.handleReturnValue 实际上调用的是 HandlerMethodReturnValueHandlerComposite .handleReturnValue方法,代码分析如下:
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//寻找能够处理returnType类型的HandlerMethodReturnValueHandler实例
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
//如果没有找到对应的HandlerMethodReturnValueHandler,则会返回一个异常
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
//用上面寻找到的handler来处理返回结果
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
从上面的代码看出,处理返回结果分为两个步骤:①、寻找能够处理 returnType 类型的HandlerMethodReturnValueHandler,如果没有找到,则会抛出异常;②、利用已经找到的handler来处理返回结果;
同时,我们要注意HandlerMethodReturnValueHandler是一个接口类型,该接口只有两个方法:boolean supportsReturnType(MethodParameter returnType)和void handleReturnValue(…)。看看它有多少个实现类:
既然是这样,我们就像看看到底是如何确定处理返回结果的handler的呢??同时,又能够处理多少种不同的handler返回类型呢??
看看 getReturnValueHandler(returnType) 方法的源代码:
/**
* Find a registered HandlerMethodReturnValueHandler that supports the given return type
*/
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
/*
* returnValueHandlers是List类型,
* 在调试的时候可以看到它共包含了13个对象。还可以增减吗??如何注
* 册一个returnValueHandler呢??有待探究。
*/
for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
returnType.getGenericParameterType() + "]");
}
if (returnValueHandler.supportsReturnType(returnType)) {
return returnValueHandler;
}
}
return null;
}
我们可以看到,在 getReturnValueHandler 方法中会遍历 returnValueHandlers (实际上是一个 List 类型),如果遇到能够支持返回值类型的handler,则将其返回。在调试中看看 returnValueHandlers 链表中都有哪些对象:
可以看见, returnValueHandlers 链表中一共有14个对象,对应于上面说的HandlerMethodReturnValueHandler接口实现类中除了 HandlerMethodReturnValueHandlerComposite 类之外的其它实现类对象。这个有点儿意思哈(_)。
如果在request请求处理的handler方法中返回String类型,则其 getReturnValueHandler(returnType) 方法就会返回一个ViewNameMethodReturnValueHandler类型的实例。看看ViewNameMethodReturnValueHandler类的定义:
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
private String[] redirectPatterns;
public void setRedirectPatterns(String... redirectPatterns) {
this.redirectPatterns = redirectPatterns;
}
public String[] getRedirectPatterns() {
return this.redirectPatterns;
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class> paramType = returnType.getParameterType();
return (void.class.equals(paramType) || String.class.equals(paramType));
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
return;
}
else if (returnValue instanceof String) {
String viewName = (String) returnValue;
// 对mavContainer的设置就是将view属性赋值为returnValue
mavContainer.setViewName(viewName);
/* 如果returnValue指示为redirect(比如说"redirect:"前缀,redirect pattern的设置等)
* 则将设置redirectModelScenario为true。
*/
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
/**
* Whether the given view name is a redirect view reference.
* The default implementation checks the configured redirect patterns and
* also if the view name starts with the "redirect:" prefix.
* @param viewName the view name to check, never {@code null}
* @return "true" if the given view name is recognized as a redirect view
* reference; "false" otherwise.
*/
protected boolean isRedirectViewName(String viewName) {
if (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName)) {
return true;
}
return viewName.startsWith("redirect:");
}
}
到现在为止,request请求的handler方法也调用了。同时根据handler方法的返回结果调用不同的Xxx ReturnValueHandler的 handleReturnValue 方法对mavContainer的view及其状态进行了设置。
我们分析出: 如果handler方法的返回类型是String的话 , ①设置mavContainer的Object view为该返回字符串;②如果返回字符串中以“redirect:”开头,则设置redirectModelScenario为true (也可以通过redirect pattern来设置,这种方式目前还不熟悉??)。
接下来就是通过设置好的 mavContainer 来得到一个ModelAndView。
最后就是 分析 【代码片段2】 中 step3:
具体就是执行 return getModelAndView(mavContainer, modelFactory, webRequest) 返回一个ModelAndView,成功的将handler的各种类型的返回结果转换成ModelAndView。
看看 getModelAndView 源代码,分析其流程,我们可以再反过来推step2中还需要做哪些设置:
1 private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
2 ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
3
4 modelFactory.updateModel(webRequest, mavContainer);
5 if (mavContainer.isRequestHandled()) {
6 return null;
7 }
8
9 //得到defaultModel或者是redirectModel
10 ModelMap model = mavContainer.getModel();
11
12 /*
13 * 注意mavContainer.getViewName()方法,如果mavContainer的Object view是一个String类型,
14 * 则返回该字符串;如果不是,则得到null
15 */
16 ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
17
18 if (!mavContainer.isViewReference()) {
19 mav.setView((View) mavContainer.getView());
20 }
21 if (model instanceof RedirectAttributes) {
22 Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
23 HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
24 RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
25 }
26 return mav;
27 }