zoukankan      html  css  js  c++  java
  • spring mvc中DispatcherServlet如何得到ModelAndView的

    首先看下面这种张图,这张图说明了spring mvc整体的流程。

    本文讲的就是如何从DispatcherServlet中得到ModerAndView的过程。

    首先看DispatherServlet这个类的doService方法,学过servlet的人都知道,它是web容器处理请求的入口。

     1 protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
     2         if (logger.isDebugEnabled()) {
     3             String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
     4             logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
     5                     " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
     6         }
     7 
     8         // Keep a snapshot of the request attributes in case of an include,
     9         // to be able to restore the original attributes after the include.
    10         Map<String, Object> attributesSnapshot = null;
    11         if (WebUtils.isIncludeRequest(request)) {
    12             attributesSnapshot = new HashMap<String, Object>();
    13             Enumeration<?> attrNames = request.getAttributeNames();
    14             while (attrNames.hasMoreElements()) {
    15                 String attrName = (String) attrNames.nextElement();
    16                 if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
    17                     attributesSnapshot.put(attrName, request.getAttribute(attrName));
    18                 }
    19             }
    20         }
    21 
    22         // Make framework objects available to handlers and view objects.
    23         request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
    24         request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    25         request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    26         request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
    27 
    28         FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
    29         if (inputFlashMap != null) {
    30             request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
    31         }
    32         request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
    33         request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    34 
    35         try {
    36             doDispatch(request, response);
    37         }
    38         finally {
    39             if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
    40                 return;
    41             }
    42             // Restore the original attribute snapshot, in case of an include.
    43             if (attributesSnapshot != null) {
    44                 restoreAttributesAfterInclude(request, attributesSnapshot);
    45             }
    46         }
    47     }

    可以看到,doService做的事情一共就两件,一是保存request的Attribute并在框架处理完之后恢复回去,以防止attribute在框架处理的过程中被迫坏;第二件事就是在第36行调用doDispatch,进入真正的spring mvc流程中。我们来看doDispatch函数,这个函数是DispatchServlet控制整个spring mvc流程的核心,文章开始的那张图所描述的流程都是在这个函数中完成的。我们来看一下这个函数。

     1 /**
     2      * Process the actual dispatching to the handler.
     3      * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
     4      * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
     5      * to find the first that supports the handler class.
     6      * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
     7      * themselves to decide which methods are acceptable.
     8      * @param request current HTTP request
     9      * @param response current HTTP response
    10      * @throws Exception in case of any kind of processing failure
    11      */
    12     protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    13         HttpServletRequest processedRequest = request;
    14         HandlerExecutionChain mappedHandler = null;
    15         boolean multipartRequestParsed = false;
    16 
    17         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    18 
    19         try {
    20             ModelAndView mv = null;
    21             Exception dispatchException = null;
    22 
    23             try {
    24                 processedRequest = checkMultipart(request);
    25                 multipartRequestParsed = (processedRequest != request);
    26 
    27                 // Determine handler for the current request.
    28                 mappedHandler = getHandler(processedRequest);
    29                 if (mappedHandler == null || mappedHandler.getHandler() == null) {
    30                     noHandlerFound(processedRequest, response);
    31                     return;
    32                 }
    33 
    34                 // Determine handler adapter for the current request.
    35                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    36 
    37                 // Process last-modified header, if supported by the handler.
    38                 String method = request.getMethod();
    39                 boolean isGet = "GET".equals(method);
    40                 if (isGet || "HEAD".equals(method)) {
    41                     long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
    42                     if (logger.isDebugEnabled()) {
    43                         logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
    44                     }
    45                     if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
    46                         return;
    47                     }
    48                 }
    49 
    50                 if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    51                     return;
    52                 }
    53 
    54                 try {
    55                     // Actually invoke the handler.
    56                     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    57                 }
    58                 finally {
    59                     if (asyncManager.isConcurrentHandlingStarted()) {
    60                         return;
    61                     }
    62                 }
    63 
    64                 applyDefaultViewName(request, mv);
    65                 mappedHandler.applyPostHandle(processedRequest, response, mv);
    66             }
    67             catch (Exception ex) {
    68                 dispatchException = ex;
    69             }
    70             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    71         }
    72         catch (Exception ex) {
    73             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    74         }
    75         catch (Error err) {
    76             triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
    77         }
    78         finally {
    79             if (asyncManager.isConcurrentHandlingStarted()) {
    80                 // Instead of postHandle and afterCompletion
    81                 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
    82                 return;
    83             }
    84             // Clean up any resources used by a multipart request.
    85             if (multipartRequestParsed) {
    86                 cleanupMultipart(processedRequest);
    87             }
    88         }
    89     }

    这个方法的核心工作内容包括这么几件:处理http请求得到模型和视图(存在一个ModelAndView对象中)、处理拦截器、渲染视图、卸载multipart内容,以及获取用于处理模型所需的处理器HanderExecutionChain和适配器HandlerAdapter。

    http的请求具体会被怎么处理将取决于处理器执行器HanderExecutionChain和适配器HandlerAdapter,所以我们先看这两个对象是怎么得到的。方法注释的已经大致说明了这两个对象是如何得到的:HanderExecutionChain是通过应用servlet所拥有的HanderMapping生成的,HandlerAdapter是通过依次查询servlet所拥有的适配器中能够支持执行器的适配器得到的。 

    那么我们就来看一下HanderExecutionChain和HandlerAdapter到底是怎么得到的。

    看28行的getHandler,HanderExecutionChain是从这里获取的,跟进去看:

     1 protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
     2         for (HandlerMapping hm : this.handlerMappings) {
     3             if (logger.isTraceEnabled()) {
     4                 logger.trace(
     5                         "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
     6             }
     7             HandlerExecutionChain handler = hm.getHandler(request);
     8             if (handler != null) {
     9                 return handler;
    10             }
    11         }
    12         return null;
    13     }

    看第7行,真的是通过DistpacherServlet的HandlerMappings得到的,跟进去看怎么得到的:

    发现这个servlet拥有的一个HM是AbstractHanderMethondMapping对象,跟进他的方法,发现它在第3行获取了一个HandlerMethod(名为handler的object对象)。

     1 @Override
     2     public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
     3         Object handler = getHandlerInternal(request);
     4         if (handler == null) {
     5             handler = getDefaultHandler();
     6         }
     7         if (handler == null) {
     8             return null;
     9         }
    10         // Bean name or resolved handler?
    11         if (handler instanceof String) {
    12             String handlerName = (String) handler;
    13             handler = getApplicationContext().getBean(handlerName);
    14         }
    15         return getHandlerExecutionChain(handler, request);
    16     }

    这个HandlerMethod对象存的是什么呢?我们看看:

    原来是它封装了我们映射到的控制器,包括bean,以及map到的方法。有了这个封装对象,剩下的事情就好办了。

    继续往下看便到了第15行,跟进去getHandlerExecutionChain方法:

     1 protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
     2         HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
     3                 (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
     4         chain.addInterceptors(getAdaptedInterceptors());
     5 
     6         String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
     7         for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
     8             if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
     9                 chain.addInterceptor(mappedInterceptor.getInterceptor());
    10             }
    11         }
    12 
    13         return chain;
    14     }

    可以看到,这个方法new了HandlerExecutionChain对象,并且把HM的拦截器和用户的拦截去都加进去了。至此HandlerExecutionChain的获取过程就讲完了。

    接下来看适配器HandlerAdapter是怎么来的。

     1     protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
     2         for (HandlerAdapter ha : this.handlerAdapters) {
     3             if (logger.isTraceEnabled()) {
     4                 logger.trace("Testing handler adapter [" + ha + "]");
     5             }
     6             if (ha.supports(handler)) {
     7                 return ha;
     8             }
     9         }
    10         throw new ServletException("No adapter for handler [" + handler +
    11                 "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
    12     }

    真的就是看servlet有那些适配器,然后一个个查询是否支持,最后返回。

    HanderExecutionChain和HandlerAdapter都有了,那么接下来就要看它们是怎么获取到模型和视图了。

    跟进去dispatch方法的56行,一路下去,来到了 ModelAndView invokeHandleMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)  方法,这个方法是HandlerAdapter适配器的方法啊, 来看看这个方法:

     1 private ModelAndView invokeHandleMethod(HttpServletRequest request,
     2             HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
     3 
     4         ServletWebRequest webRequest = new ServletWebRequest(request, response);
     5 
     6         WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
     7         ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
     8         ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
     9 
    10         ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    11         mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    12         modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
    13         mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    14 
    15         AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
    16         asyncWebRequest.setTimeout(this.asyncRequestTimeout);
    17 
    18         final WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    19         asyncManager.setTaskExecutor(this.taskExecutor);
    20         asyncManager.setAsyncWebRequest(asyncWebRequest);
    21         asyncManager.registerCallableInterceptors(this.callableInterceptors);
    22         asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
    23 
    24         if (asyncManager.hasConcurrentResult()) {
    25             Object result = asyncManager.getConcurrentResult();
    26             mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
    27             asyncManager.clearConcurrentResult();
    28 
    29             if (logger.isDebugEnabled()) {
    30                 logger.debug("Found concurrent result value [" + result + "]");
    31             }
    32             requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
    33         }
    34 
    35         requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
    36 
    37         if (asyncManager.isConcurrentHandlingStarted()) {
    38             return null;
    39         }
    40 
    41         return getModelAndView(mavContainer, modelFactory, webRequest);
    42     }

    终于知道为什么需要适配器了,原来这个适配器的方法中,就适配了spring mvc和servlet,还有几个适配对象,其中最重要的是ServletWebRequest webRequest ,它适配了request。另外还有ModelAndViewContainer mavContainer,通过它持有模型和视图。

    另外看这三行,这三行采用了工厂模式。

    第一行获取了数据绑定的工厂,最重要的是它有handlerMethod。然后它会传给第三行,得到ServletInvocableHandlerMethod requestMappingMethod 。这个 requestMappingMethod 就是我们进入控制器调用的关键。

    第二行获取了模型工厂,有了它,就可以创建模型了。

    值得注意的是,这三个对象,每个里面都有handlerMethod的信息。

    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);

    看第35行,requestMappingMethod.invokeAndHandle(webRequest, mavContainer),这里开始真正的填充模型了,怎么填充呢?其实猜都能猜到,首先它自己有handlermethod,又传入了webRequest和mavContainer,很容易想到,它肯定是根据请求,通过反射获取控制器的requestMap方法,将参数传入并调用方法,最后得到控制器处理后的模型,并得到控制器指定的视图。至于传入的参数是如何得到的,我在另一篇文章李有比较详细的描述,请看spring mvc中的控制器方法中的参数从哪里传进来这篇文章。

    看起来貌似已经把整个流程讲完了,但是等等,有个很重要的问题,生成处理器执行器HandlerExecutionChain的时候需要从HM里get到一个真正的处理器传入给HandlerExecutionChain的构造器。这个处理器怎么来的?这个带着这个疑问,我们继续来看他们是怎么得到的。

    前面已经说了,这个handler是在HandlerExecutionChain getHandler(HttpServletRequest request) 的第三行调用HM的Object handler = getHandlerInternal(request)得到的,我们看一看getHandlerInternal这个函数:

     1 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
     2         String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
     3         if (logger.isDebugEnabled()) {
     4             logger.debug("Looking up handler method for path " + lookupPath);
     5         }
     6         HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
     7         if (logger.isDebugEnabled()) {
     8             if (handlerMethod != null) {
     9                 logger.debug("Returning handler method [" + handlerMethod + "]");
    10             }
    11             else {
    12                 logger.debug("Did not find handler method for [" + lookupPath + "]");
    13             }
    14         }
    15         return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    16     }

    可以看到,getHandlerInternal是通过请求的路径来查询得到那个关键的处理器(在这里是一个HandlerMethod,它含有控制器的requestMap方法签名),再看一下第6行,它是怎么通过路径查询到处理器的,跟进去lookupHandlerMethod函数:

     1 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
     2         List<Match> matches = new ArrayList<Match>();
     3         List<T> directPathMatches = this.urlMap.get(lookupPath);
     4         if (directPathMatches != null) {
     5             addMatchingMappings(directPathMatches, matches, request);
     6         }
     7         if (matches.isEmpty()) {
     8             // No choice but to go through all mappings...
     9             addMatchingMappings(this.handlerMethods.keySet(), matches, request);
    10         }
    11 
    12         if (!matches.isEmpty()) {
    13             Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
    14             Collections.sort(matches, comparator);
    15             if (logger.isTraceEnabled()) {
    16                 logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
    17             }
    18             Match bestMatch = matches.get(0);
    19             if (matches.size() > 1) {
    20                 Match secondBestMatch = matches.get(1);
    21                 if (comparator.compare(bestMatch, secondBestMatch) == 0) {
    22                     Method m1 = bestMatch.handlerMethod.getMethod();
    23                     Method m2 = secondBestMatch.handlerMethod.getMethod();
    24                     throw new IllegalStateException(
    25                             "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
    26                             m1 + ", " + m2 + "}");
    27                 }
    28             }
    29             handleMatch(bestMatch.mapping, lookupPath, request);
    30             return bestMatch.handlerMethod;
    31         }
    32         else {
    33             return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
    34         }
    35     }

    lookupHandlerMethod函数把获取的处理器包装成一个个Match,并对Match进行了排序(因为一个path可能不仅仅对应一个处理器,比如存在两个requestMap路径相同的函数,那么一个path就会对应两个处理器)之后取最匹配的那个。在这个过程中甚至还做了重复性检测,如果有两个一模一样的处理器存在,那说明我们的控制器写的有歧义了,直接抛出异常。我们看到第5行 addMatchingMappings(directPathMatches, matches, request); ,这里是真正获取HandlerMethod的地方,跟进去看:

    1     private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    2         for (T mapping : mappings) {
    3             T match = getMatchingMapping(mapping, request);
    4             if (match != null) {
    5                 matches.add(new Match(match, this.handlerMethods.get(mapping)));
    6             }
    7         }
    8     }

    直接看到第5行 matches.add(new Match(match, this.handlerMethods.get(mapping))) ,这里传入了一个重要的this.handlerMethods.get(mapping),this.handlerMethods 是一个LinkedHashMap,存放了此HM所有的处理器,根据mapping对象进行索引(mapping对象标识了唯一的请求映射),看一看这个handlerMethods 里有啥:

    果然我们写的控制器方法对应的handler,都在这里头保存着。

    接下来我们看看,这些handler是怎么保存到HM中的。我们在HM中找到了一个initHandlerMethods方法,从名字中就可以看出来这个方法是初始化methods用的,我们看看这个方法的内容:

     1 protected void initHandlerMethods() {
     2     if (logger.isDebugEnabled()) {
     3         logger.debug("Looking for request mappings in application context: " + getApplicationContext());
     4     }
     5 
     6     String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
     7             BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
     8             getApplicationContext().getBeanNamesForType(Object.class));
     9 
    10     for (String beanName : beanNames) {
    11         if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
    12                 isHandler(getApplicationContext().getType(beanName))){
    13             detectHandlerMethods(beanName);
    14         }
    15     }
    16     handlerMethodsInitialized(getHandlerMethods());
    17 }

     它从ApplicationContext中获取了bean的名字,然后根据名字,在第13行中通过 detectHandlerMethods(beanName) 得到真正的bean:

     1 protected void detectHandlerMethods(final Object handler) {
     2         Class<?> handlerType =
     3                 (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
     4 
     5         // Avoid repeated calls to getMappingForMethod which would rebuild RequestMappingInfo instances
     6         final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
     7         final Class<?> userType = ClassUtils.getUserClass(handlerType);
     8 
     9         Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
    10             @Override
    11             public boolean matches(Method method) {
    12                 T mapping = getMappingForMethod(method, userType);
    13                 if (mapping != null) {
    14                     mappings.put(method, mapping);
    15                     return true;
    16                 }
    17                 else {
    18                     return false;
    19                 }
    20             }
    21         });
    22 
    23         for (Method method : methods) {
    24             registerHandlerMethod(handler, method, mappings.get(method));
    25         }
    26     }

    看第24行,在这里注册HM所持的HandlerMethod,跟进去:

     1 protected void registerHandlerMethod(Object handler, Method method, T mapping) {
     2         HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
     3         HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
     4         if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
     5             throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
     6                     "' bean method 
    " + newHandlerMethod + "
    to " + mapping + ": There is already '" +
     7                     oldHandlerMethod.getBean() + "' bean method
    " + oldHandlerMethod + " mapped.");
     8         }
     9 
    10         this.handlerMethods.put(mapping, newHandlerMethod);
    11         if (logger.isInfoEnabled()) {
    12             logger.info("Mapped "" + mapping + "" onto " + newHandlerMethod);
    13         }
    14 
    15         Set<String> patterns = getMappingPathPatterns(mapping);
    16         for (String pattern : patterns) {
    17             if (!getPathMatcher().isPattern(pattern)) {
    18                 this.urlMap.add(pattern, mapping);
    19             }
    20         }
    21     }

    看第10行 this.handlerMethods.put(mapping, newHandlerMethod) ,一个个Put 进去了,这样,就完成了HandlerMethod的注册。在第18行 this.urlMap.add(pattern, mapping) ,完成了路径到mapping的映射。

    最后只要框架在启动是调用initMethod方法,就可以完成处理器的注册了。

    自此,DispatcherServlet如何得到ModelAndView的过程就讲完了。

  • 相关阅读:
    缓存(二)
    缓存
    SQL Server 导入大数据脚本
    C#执行OracleHelper
    MERGE 用法
    c# 高效读写文件
    C#网络编程
    C#常用IO流与读写文件
    注册asp.net 4.0 到iis
    javascript原生图片懒加载
  • 原文地址:https://www.cnblogs.com/JMLiu/p/10029311.html
Copyright © 2011-2022 走看看