程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Spring-MVC運行原理,spring-mvc原理

Spring-MVC運行原理,spring-mvc原理

編輯:JAVA綜合教程

Spring-MVC運行原理,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;

}

 

       

    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[]中,方法調用等。

 

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

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved