首先,我们还是从 DispatcherServlet .doDispatch( HttpServletRequest request, HttpServletResponse response) throws Exception方法开始,看看这个牛逼的ModelAndView是怎么开始的,又是怎么结束的:
1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
2 HttpServletRequest processedRequest = request;
3 HandlerExecutionChain mappedHandler = null;
4 boolean multipartRequestParsed = false;
5
6 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
7
8 try {
9 ModelAndView mv = null;
10 Exception dispatchException = null;
11
12 try {
13 processedRequest = checkMultipart(request);
14 multipartRequestParsed = (processedRequest != request);
15
16 // Determine handler for the current request.
17 mappedHandler = getHandler(processedRequest);
18 if (mappedHandler == null || mappedHandler.getHandler() == null) {
19 noHandlerFound(processedRequest, response);
20 return;
21 }
22
23 // Determine handler adapter for the current request.
24 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
25
26 // Process last-modified header, if supported by the handler.
27 String method = request.getMethod();
28 boolean isGet = "GET".equals(method);
29 if (isGet || "HEAD".equals(method)) {
30 long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
31 if (logger.isDebugEnabled()) {
32 logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
33 }
34 if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
35 return;
36 }
37 }
38
39 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
40 return;
41 }
42
43 // Actually invoke the handler.调用handler方法,返回ModelAndView类型的对象
44 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
45
46 if (asyncManager.isConcurrentHandlingStarted()) {
47 return;
48 }
49 // 设置mv的Object view属性值,是一个String类型(依据request的URI计算,加上一个前缀和后缀得到)
50 applyDefaultViewName(request, mv);
51 mappedHandler.applyPostHandle(processedRequest, response, mv);
52 }
53 catch (Exception ex) {
54 dispatchException = ex;
55 }
56 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
57 }
58 catch (Exception ex) {
59 triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
60 }
61 catch (Error err) {
62 triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
63 }
64 finally {
65 if (asyncManager.isConcurrentHandlingStarted()) {
66 // Instead of postHandle and afterCompletion
67 if (mappedHandler != null) {
68 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
69 }
70 }
71 else {
72 // Clean up any resources used by a multipart request.
73 if (multipartRequestParsed) {
74 cleanupMultipart(processedRequest);
75 }
76 }
77 }
78 }
从上面的源代码可以看出,mv是在调用handler方法的时候返回的(即便,我们@RequestMapping注解标识的handler方法很多时候返回的是一个String,或者是View类型。但是,反射是无所不能的,你懂的...)。我们知道,@RequestMapping标注的handler方法通常能够返回三种类型的结果:String,ModelAndView和View。从上面的ha.handle方法我们知道,动态代理最后都会将结果转化成ModelAndView类型。
一、得到mv
下面对 mv = ha.handle(processedRequest, response, mappedHandler.getHandler())方法中如何得到mv实例的演变过程进行分析:
从 7、SpringMVC源码分析(2) 中分析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 的源代码:
【代码片段2】:
1 /**
2 * Invoke the RequestMapping handler method preparing a ModelAndView if view resolution is required.
3 */
4 private ModelAndView invokeHandleMethod(HttpServletRequest request,
5 HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
6
7 //step 1、ModelAndView的前世:是一个ModelAndViewContainer实例
8 ModelAndViewContainer mavContainer = new ModelAndViewContainer();
9
10 /*
11 * RequestContextUtils.getInputFlashMap(request)可以获取到request中的attribute,
12 * 并且将所有的request中的attribute放置在mavContainer中,此时使用的是defaultModel
13 */
14 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
15 modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
16 /*
17 * 在RequestMappingHandlerAdapter中ignoreDefaultModelOnRedirect默认为false
18 */
19 mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
20
21 //省略许多代码...
22
23 if (asyncManager.hasConcurrentResult()) {
24 //...
25 mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
26
27 //...
28 }
29
30 //step 2、调用handler方法,
31 requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
32
33 //省略许多代码...
34
35 //step 3、从mavContainer中获取到ModelAndView实例,返回
36 return getModelAndView(mavContainer, modelFactory, webRequest);
37 }
我们看看 【代码片段2】 中 step1:
ModelAndViewContainer是个什么东西?同样从 7、SpringMVC源码分析(2) 的step1 中我们可以得出如下的结论:
①、 ModelMap实际上就是一个LinkedHashMap
②、 ModelAndViewContainer含有两个ModelMap对象 : defaultModel和redirectModel,默认情况下使用defaultModel ;
有了以上两个结论,我们还有必要分析一下ModelAndViewContainer的一些细节(主要参看下面代码中的注释):
【代码片段3】:
1 public class ModelAndViewContainer {
2 /*
3 * 对这7个属性我们可以将其分为3个部分来看
4 */
5
6 // 1、两个模型和一个view,其中view可以存放任何java类型
7 private Object view;
8
9 // * 注意这个是final类型,也就是不能再重新为defaultModel赋值
10 private final ModelMap defaultModel = new BindingAwareModelMap();
11
12 private ModelMap redirectModel;
13
14 // 2、记录当前的Model使用策略
15 private boolean ignoreDefaultModelOnRedirect = false;
16
17 private boolean redirectModelScenario = false;
18
19 // 3、记录request和session的处理状态
20 private final SessionStatus sessionStatus = new SimpleSessionStatus();
21
22 private boolean requestHandled = false;
23
24 //和view属性相关的处理方法
25 /*
26 * 对view的处理主要分为String类型和非String类型。如果views是一个
27 * String类型的对象,那么它就是下面说的isViewReference
28 */
29 public void setViewName(String viewName) {
30 this.view = viewName;
31 }
32
33 public String getViewName() {
34 return (this.view instanceof String ? (String) this.view : null);
35 }
36
37 public void setView(Object view) {
38 this.view = view;
39 }
40
41 public Object getView() {
42 return this.view;
43 }
44
45 /*
46 * 注意,如果veiw是一个String类型,那么isViewReference
47 */
48 public boolean isViewReference() {
49 return (this.view instanceof String);
50 }
51
52 //和model相关的处理方法
53 /*
54 * 假设redirectModelScenario = R ,ignoreDefaultModelOnRedirect = I ,(redirectModel == null)= M
55 * 那么(R, I, M)共有8中组合情况,useDefaultModel返回false(也就是使用redirectModel)只有三种情况:
56 * (1,1,0)、(1,1,1)、(1,0,0)
57 * a:如果同时设置了redirectModelScenario和ignoreDefaultModelOnRedirect为true,那么无论redirectModel
58 * 是否为null,都会使用redirectModel;
59 * b:如果设置了redirectModelScenario为true,而ignoreDefaultModelOnRedirect为false,同时redirectModel
60 * 为null,那么也会使用redirectModel;
61 */
62 private boolean useDefaultModel() {
63 return (!this.redirectModelScenario || (!this.ignoreDefaultModelOnRedirect && this.redirectModel == null));
64 }
65
66 /*
67 * 这个方法是重要的,通过设置redirectModelScenario和ignoreDefaultModelOnRedirect来影响该方法的返回值;
68 * 如果没有初始化redirectModel,那么就会new一个ModelMap对象进行返回;
69 */
70 public ModelMap getModel() {
71 if (useDefaultModel()) {
72 return this.defaultModel;
73 }
74 else {
75 return (this.redirectModel != null) ? this.redirectModel : new ModelMap();
76 }
77 }
78
79 public ModelMap getDefaultModel() {
80 return this.defaultModel;
81 }
82
83 public void setRedirectModel(ModelMap redirectModel) {
84 this.redirectModel = redirectModel;
85 }
86
87 /**
88 * Whether the controller has returned a redirect instruction, e.g. a
89 * "redirect:" prefixed view name, a RedirectView instance, etc.
90 */
91 public void setRedirectModelScenario(boolean redirectModelScenario) {
92 this.redirectModelScenario = redirectModelScenario;
93 }
94
95 public void setIgnoreDefaultModelOnRedirect(boolean ignoreDefaultModelOnRedirect) {
96 this.ignoreDefaultModelOnRedirect = ignoreDefaultModelOnRedirect;
97 }
98
99 //省略一些方法...
100 }
接下来分析 【代码片段2】 中 step2:
看看 requestMappingMethod.invokeAndHandle(webRequest, mavContainer); 中对mavContainer做了什么事情?看看 requestMappingMethod.invokeAndHandle(webRequest, mavContainer) 的源代码:
【代码片段4】:
1 /**
2 *Invokes the method and handles the return value through one of the configured HandlerMethodReturnValueHandlers.
3 */
4 public void invokeAndHandle(ServletWebRequest webRequest,
5 ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
6
7 // 调用handler方法,的到返回结果为returnValue
8 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
9 setResponseStatus(webRequest);
10
11 if (returnValue == null) {//handler返回null
12 if (isRequestNotModified(webRequest) || hasResponseStatus() || mavContainer.isRequestHandled()) {
13 mavContainer.setRequestHandled(true);
14 return;
15 }
16 }else if (StringUtils.hasText(this.responseReason)) {
17 mavContainer.setRequestHandled(true);
18 return;
19 }
20
21 // 设置标志位,表示当前request请求还没有处理完成
22 mavContainer.setRequestHandled(false);
23 try {
24 // 处理返回结果returnValue
25 this.returnValueHandlers.handleReturnValue(
26 returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
27 }catch (Exception ex) {
28 if (logger.isTraceEnabled()) {
29 logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
30 }
31 throw ex;
32 }
33 }
看看上面代码中是如何处理返回结果returnValue的, this .returnValueHandlers.handleReturnValue 实际上调用的是 HandlerMethodReturnValueHandlerComposite .handleReturnValue方法,代码分析如下:
【代码片段5】:
1 public void handleReturnValue(Object returnValue, MethodParameter returnType,
2 ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
3
4 //寻找能够处理returnType类型的HandlerMethodReturnValueHandler实例
5 HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
6
7 //如果没有找到对应的HandlerMethodReturnValueHandler,则会返回一个异常
8 Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
9
10 //用上面寻找到的handler来处理返回结果
11 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
12 }
从上面的代码看出,处理返回结果分为两个步骤:①、寻找能够处理 returnType 类型的HandlerMethodReturnValueHandler,如果没有找到,则会抛出异常;②、利用已经找到的handler来处理返回结果;
同时,我们要注意HandlerMethodReturnValueHandler是一个接口类型,该接口只有两个方法:boolean supportsReturnType(MethodParameter returnType)和void handleReturnValue(...)。看看它有多少个实现类:
既然是这样,我们就像看看到底是如何确定处理返回结果的handler的呢??同时,又能够处理多少种不同的handler返回类型呢??
看看 getReturnValueHandler(returnType) 方法的源代码:
【代码片段6】:
1 /**
2 * Find a registered HandlerMethodReturnValueHandler that supports the given return type
3 */
4 private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
5 /*
6 * returnValueHandlers是List类型,
7 * 在调试的时候可以看到它共包含了13个对象。还可以增减吗??如何注
8 * 册一个returnValueHandler呢??有待探究。
9 */
10 for (HandlerMethodReturnValueHandler returnValueHandler : returnValueHandlers) {
11 if (logger.isTraceEnabled()) {
12 logger.trace("Testing if return value handler [" + returnValueHandler + "] supports [" +
13 returnType.getGenericParameterType() + "]");
14 }
15 if (returnValueHandler.supportsReturnType(returnType)) {
16 return returnValueHandler;
17 }
18 }
19 return null;
20 }
我们可以看到,在 getReturnValueHandler 方法中会遍历 returnValueHandlers (实际上是一个 List
可以看见, returnValueHandlers 链表中一共有14个对象,对应于上面说的HandlerMethodReturnValueHandler接口实现类中除了 HandlerMethodReturnValueHandlerComposite 类之外的其它实现类对象。这个有点儿意思哈(^_^)。
如果在request请求处理的handler方法中返回String类型,则其 getReturnValueHandler(returnType) 方法就会返回一个ViewNameMethodReturnValueHandler类型的实例。看看ViewNameMethodReturnValueHandler类的定义:
【代码片段7】:
1 public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
2
3 private String[] redirectPatterns;
4
5 public void setRedirectPatterns(String... redirectPatterns) {
6 this.redirectPatterns = redirectPatterns;
7 }
8
9 public String[] getRedirectPatterns() {
10 return this.redirectPatterns;
11 }
12
13
14 @Override
15 public boolean supportsReturnType(MethodParameter returnType) {
16 Class> paramType = returnType.getParameterType();
17 return (void.class.equals(paramType) || String.class.equals(paramType));
18 }
19
20 @Override
21 public void handleReturnValue(Object returnValue, MethodParameter returnType,
22 ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
23
24 if (returnValue == null) {
25 return;
26 }
27 else if (returnValue instanceof String) {
28 String viewName = (String) returnValue;
29
30 // 对mavContainer的设置就是将view属性赋值为returnValue
31 mavContainer.setViewName(viewName);
32
33 /* 如果returnValue指示为redirect(比如说"redirect:"前缀,redirect pattern的设置等)
34 * 则将设置redirectModelScenario为true。
35 */
36 if (isRedirectViewName(viewName)) {
37 mavContainer.setRedirectModelScenario(true);
38 }
39 }
40 else {
41 // should not happen
42 throw new UnsupportedOperationException("Unexpected return type: " +
43 returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
44 }
45 }
46
47 /**
48 * Whether the given view name is a redirect view reference.
49 * The default implementation checks the configured redirect patterns and
50 * also if the view name starts with the "redirect:" prefix.
51 * @param viewName the view name to check, never {@code null}
52 * @return "true" if the given view name is recognized as a redirect view
53 * reference; "false" otherwise.
54 */
55 protected boolean isRedirectViewName(String viewName) {
56 if (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName)) {
57 return true;
58 }
59 return viewName.startsWith("redirect:");
60 }
61
62 }
到现在为止,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中还需要做哪些设置:
【代码片段8】:
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 }