码迷,mamicode.com
首页 > 编程语言 > 详细

8、SpringMVC源码分析(3):分析ModelAndView的形成过程

时间:2015-11-19 22:23:07      阅读:467      评论:0      收藏:0      [点我收藏+]

标签:

  

  首先,我们还是从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<String, Object>,可以用于存放key-value的集合;

  ②、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<HandlerMethodReturnValueHandler>类型,
 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<HandlerMethodReturnValueHandler>类型),如果遇到能够支持返回值类型的handler,则将其返回。在调试中看看returnValueHandlers链表中都有哪些对象:

技术分享

  可以看见,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方法的返回结果调用不同的XxxReturnValueHandler的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<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
23         HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
24         RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
25     }
26     return mav;
27 }

 

 

 

 

 

  

 

8、SpringMVC源码分析(3):分析ModelAndView的形成过程

标签:

原文地址:http://www.cnblogs.com/lj95801/p/4978255.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!