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方法。

原文地址:https://www.cnblogs.com/swave/p/5973303.html