• Spring-MVC运行原理


    一、 Spring-MVC的对象初始化,即 bean放入context的beanFactory中。

           1. 对象的初始化工作主要在org.springframework.web.servlet.FrameworkServlet类中的initServletBean方法中完成,initServletBean方法最终会调用到

               org.springframework.context.support.AbstractApplicationContext类的refresh方法,refresh方法是主要的bean的初始化方法。refresh方法又调用类里面的obtainFreshBeanFactory方法。

           2.  org.springframework.beans.factory.support.DefaultListableBeanFactory为默认的BeanFactory,

                DefaultListableBeanFactory 类中的registerBeanDefinition方法为保存对象列表信息的主要方法,beanFactory中的对象存放到成员变量Map<String, BeanDefinition> beanDefinitionMap中。

     1 private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
     2 
     3     //---------------------------------------------------------------------
     4     // Implementation of BeanDefinitionRegistry interface
     5     //---------------------------------------------------------------------
     6 
     7     public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
     8             throws BeanDefinitionStoreException {
     9 
    10         Assert.hasText(beanName, "Bean name must not be empty");
    11         Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    12 
    13         if (beanDefinition instanceof AbstractBeanDefinition) {
    14             try {
    15                 ((AbstractBeanDefinition) beanDefinition).validate();
    16             }
    17             catch (BeanDefinitionValidationException ex) {
    18                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    19                         "Validation of bean definition failed", ex);
    20             }
    21         }
    22 
    23         synchronized (this.beanDefinitionMap) {
    24             Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
    25             if (oldBeanDefinition != null) {
    26                 if (!this.allowBeanDefinitionOverriding) {
    27                     throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    28                             "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
    29                             "': There is already [" + oldBeanDefinition + "] bound.");
    30                 }
    31                 else {
    32                     if (this.logger.isInfoEnabled()) {
    33                         this.logger.info("Overriding bean definition for bean '" + beanName +
    34                                 "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
    35                     }
    36                 }
    37             }
    38             else {
    39                 this.beanDefinitionNames.add(beanName);
    40                 this.frozenBeanDefinitionNames = null;
    41             }
    42             this.beanDefinitionMap.put(beanName, beanDefinition);
    43         }
    44 
    45         resetBeanDefinition(beanName);
    46     }

    二、   Spring-MVC中Controller中的method与 RequestMappingURL的初始化

          1.  Controller中的method与 RequestMappingURL的映射关系绑定初始化工作主要在spring-webmvc.jar中完成。这个jar 文件包含Spring MVC 框架相关的所有类,

                包括框架的Servlets,Web MVC框架,控制器和视图支持。

          2.   SpringMVC在容器初始化时,绑定请求URL映射到相应的Controller中的方法的工作主要在org.springframework.web.servlet.handler.AbstractHandlerMethodMapping类中的initHandlerMethods方法完成,

               AbstractHandlerMethodMapping实现了Spring的org.springframework.beans.factory.InitializingBean接口,在InitializingBean的afterPropertiesSet即调用了initHandlerMethods。

              MappingURL与Controller对应方法的映射关系在servlet容器初始化时保存到 AbstractHandlerMethodMapping中的成员变量urlMap中。

              AbstractHandlerMethodMapping类的initHandlerMethods为protected修饰 ,可被子类重写。

    三、  请求URL到达映射处理的Controller的Method前的逻辑。

            执行业务方法的逻辑主要在org.springframework.web.servlet.DispatcherServlet类的doDispatch方法中。

            以下为doDispatch方法的代码:

           

        /**
         * Process the actual dispatching to the handler.
         * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
         * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
         * to find the first that supports the handler class.
         * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
         * themselves to decide which methods are acceptable.
         * @param request current HTTP request
         * @param response current HTTP response
         * @throws Exception in case of any kind of processing failure
         */
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            int interceptorIndex = -1;
    
            try {
                ModelAndView mv;
                boolean errorView = false;
    
                try {
                    processedRequest = checkMultipart(request);
    
                    // Determine handler for the current request.
                    mappedHandler = getHandler(processedRequest, false);
                    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()) {
                            String requestUri = urlPathHelper.getRequestUri(request);
                            logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
                        }
                        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
    
                    // Apply preHandle methods of registered interceptors.
                    HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
                    if (interceptors != null) {
                        for (int i = 0; i < interceptors.length; i++) {
                            HandlerInterceptor interceptor = interceptors[i];
                            if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
                                triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
                                return;
                            }
                            interceptorIndex = i;
                        }
                    }
    
                    // Actually invoke the handler.
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                    // Do we need view name translation?
                    if (mv != null && !mv.hasView()) {
                        mv.setViewName(getDefaultViewName(request));
                    }
    
                    // Apply postHandle methods of registered interceptors.
                    if (interceptors != null) {
                        for (int i = interceptors.length - 1; i >= 0; i--) {
                            HandlerInterceptor interceptor = interceptors[i];
                            interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
                        }
                    }
                }
                catch (ModelAndViewDefiningException ex) {
                    logger.debug("ModelAndViewDefiningException encountered", ex);
                    mv = ex.getModelAndView();
                }
                catch (Exception ex) {
                    Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                    mv = processHandlerException(processedRequest, response, handler, ex);
                    errorView = (mv != null);
                }
    
                // Did the handler return a view to render?
                if (mv != null && !mv.wasCleared()) {
                    render(mv, processedRequest, 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");
                    }
                }
    
                // Trigger after-completion for successful outcome.
                triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
            }
    
            catch (Exception ex) {
                // Trigger after-completion for thrown exception.
                triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
                throw ex;
            }
            catch (Error err) {
                ServletException ex = new NestedServletException("Handler processing failed", err);
                // Trigger after-completion for thrown exception.
                triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
                throw ex;
            }
    
            finally {
                // Clean up any resources used by a multipart request.
                if (processedRequest != request) {
                    cleanupMultipart(processedRequest);
                }
            }
        }

        主要执行的过程有四个步骤,如下所示:

          1. 通过调用org.springframework.web.servlet.handler.AbstractHandlerMethodMapping类的getHandlerInternal方法来获得相应的请求url的映射的controller和method的HandlerMethod,

                       AbstractHandlerMethodMapping中的成员变量urlMap保存的即为servlet容器启动时初始化的RequestMapping映射的Controller和Method的信息。

               getHandlerInternal方法代码如下:

    /**
         * Look up the best-matching handler method for the current request.
         * If multiple matches are found, the best match is selected.
         * @param lookupPath mapping lookup path within the current servlet mapping
         * @param request the current request
         * @return the best-matching handler method, or {@code null} if no match
         * @see #handleMatch(Object, String, HttpServletRequest)
         * @see #handleNoMatch(Set, String, HttpServletRequest)
         */
        protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
            List<Match> matches = new ArrayList<Match>();
    
            List<T> directPathMatches = this.urlMap.get(lookupPath);
            if (directPathMatches != null) {
                addMatchingMappings(directPathMatches, matches, request);
            }
    
            if (matches.isEmpty()) {
                // No choice but to go through all mappings
                addMatchingMappings(this.handlerMethods.keySet(), matches, request);
            }
    
            if (!matches.isEmpty()) {
                Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                Collections.sort(matches, comparator);
    
                if (logger.isTraceEnabled()) {
                    logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
                }
    
                Match bestMatch = matches.get(0);
                if (matches.size() > 1) {
                    Match secondBestMatch = matches.get(1);
                    if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                        Method m1 = bestMatch.handlerMethod.getMethod();
                        Method m2 = secondBestMatch.handlerMethod.getMethod();
                        throw new IllegalStateException(
                                "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" +
                                m1 + ", " + m2 + "}");
                    }
                }
    
                handleMatch(bestMatch.mapping, lookupPath, request);
                return bestMatch.handlerMethod;
            }
            else {
                return handleNoMatch(handlerMethods.keySet(), lookupPath, request);
            }
        }

              HandlerMethod类主要包括的信息有 匹配处理的Controller ,处理方法Method,以及方法的传入参数 parameters等信息。HandlerMethod的主要成员变量代码如下:

              

    public class HandlerMethod {
    
        /** Logger that is available to subclasses */
        protected final Log logger = LogFactory.getLog(HandlerMethod.class);
    
        private final Object bean;
    
        private final Method method;
    
        private final BeanFactory beanFactory;
    
        private MethodParameter[] parameters;
    
        private final Method bridgedMethod;
    
    }

             bean为url映射匹配到Controller, method为映射到的处理方法。

           

        2. 执行url匹配的过滤器的preHandle方法。

        3. 执行主要的业务过程处理方法,即执行步骤1中找到的Controller对应的Method。

           执行主要的业务处理方法的是在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter类的invokeHandlerMethod方法中调用。

          

        /**
         * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView} if view resolution is required.
         */
        private ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response,
                HandlerMethod handlerMethod) throws Exception {
    
            ServletWebRequest webRequest = new ServletWebRequest(request, response);
    
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
            ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);
    
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    
            requestMappingMethod.invokeAndHandle(webRequest, mavContainer);
            modelFactory.updateModel(webRequest, mavContainer);
    
            if (mavContainer.isRequestHandled()) {
                return null;
            }
            else {
                ModelMap model = mavContainer.getModel();
                ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model);
                if (!mavContainer.isViewReference()) {
                    mav.setView((View) mavContainer.getView());
                }
                if (model instanceof RedirectAttributes) {
                    Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
                    RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
                }
                return mav;
            }
        }

           业务方法调用主要在org.springframework.web.method.support.InvocableHandlerMethod类的invokeForRequest方法中,过程包括组装request的请求参数传入到handleMethod的参数数组args[]中,方法调用等。

           方法通过java的反射机制执行,即java.lang.reflect.Method.invoke(Object controller, Object... args);  参数一controller为方法所属的Controller,参数二args为请求参数

        /**
         * Invoke the method after resolving its argument values in the context of the given request. <p>Argument
         * values are commonly resolved through {@link HandlerMethodArgumentResolver}s. The {@code provideArgs}
         * parameter however may supply argument values to be used directly, i.e. without argument resolution.
         * Examples of provided argument values include a {@link WebDataBinder}, a {@link SessionStatus}, or
         * a thrown exception instance. Provided argument values are checked before argument resolvers.
         *
         * @param request the current request
         * @param mavContainer the ModelAndViewContainer for this request
         * @param providedArgs "given" arguments matched by type, not resolved
         * @return the raw value returned by the invoked method
         * @exception Exception raised if no suitable argument resolver can be found, or the method raised an exception
         */
        public final Object invokeForRequest(NativeWebRequest request,
                                             ModelAndViewContainer mavContainer,
                                             Object... providedArgs) throws Exception {
            Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    
            if (logger.isTraceEnabled()) {
                StringBuilder builder = new StringBuilder("Invoking [");
                builder.append(this.getMethod().getName()).append("] method with arguments ");
                builder.append(Arrays.asList(args));
                logger.trace(builder.toString());
            }
    
            Object returnValue = invoke(args);
    
            if (logger.isTraceEnabled()) {
                logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]");
            }
    
            return returnValue;
        }

        

        4. 执行url匹配的过滤器的postHandle方法。

  • 相关阅读:
    javascript异步编程系列【十】—Jscex+Easeljs制作坦克大战
    博客园分页JQuery打造的分页无刷新的Repeater
    参赛作品
    摄像机、投影、3D旋转、缩放
    javascript异步编程系列【八】Jscex版火拼俄罗斯
    javascript异步编程系列【七】扫盲,我们为什么要用Jscex
    javascript异步编程系列【五】Jscex制作愤怒的小鸟
    javascript异步编程系列【六】Jscex版愤怒的小鸟之冲锋陷阵鸟
    每周优秀代码赏析—Jscex内核【一】
    javascript异步编程系列【一】用Jscex画圆
  • 原文地址:https://www.cnblogs.com/swave/p/5973303.html
Copyright © 2020-2023  润新知