springMVC源碼淺析

本文涉及到代碼比較多,建議在電腦端閱讀!!!web

借用網上的一張springMVC流程圖,spring

spingMVC源碼分三部分來分析設計模式

1:ApplicationContext初始化時創建全部url和controller類的對應關係(使用map保存)數組

2:根據請求url找到對應的controller,並從controller中找處處理請求的方法mvc

3:request參數綁定到方法的形參,執行方法並處理請求,最後返回結果app

第一步:創建Map<urls,Controller>關係

咱們首先看第一個步驟,也就是創建Map<url,controller>關係的部分.ide

第一部分的入口類爲this

ApplicationObjectSupporturl

--->setApplicationContext()spa

-->initApplicationContext(context),實現子類AbstractDetectingUrlHandlerMapping實現了該方法,因此咱們直接看子類中的初始化容器


創建當前ApplicationContext中的全部controller和url的對應關係


 /**

*獲取controller中全部方法的url,由子類實現,典型的模板模式

 **/

protected abstract String[] determineUrlsForHandler(String beanName);

 

determineUrlsForHandler(String beanName)方法的做用是獲取每一個controller中的url,不一樣的子類有不一樣的實現,這是一個典型的模板設計模式(前面有一篇文章有講到模板模式),

DefaultAnnotationHandlerMapping是AbstractDetectingUrlHandlerMapping的子類,處理註解形式的url映射.因此咱們這裏以DefaultAnnotationHandlerMapping來進行分析,

接下來看看DefaultAnnotationHandlerMapping是如何查beanName上全部映射的url.

/**

   * 獲取controller中全部的url

     */

  protected String[] determineUrlsForHandler(String beanName) {

       // 獲取ApplicationContext容器

    ApplicationContext context = getApplicationContext();

        //從容器中獲取controller

     Class<?> handlerType = context.getType(beanName);

     // 獲取controller上的@RequestMapping註解

        RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);

        if (mapping != null) { // controller上有註解

            this.cachedMappings.put(handlerType, mapping);

        // 返回結果集

            Set<String> urls = new LinkedHashSet<String>();

        // controller的映射url

            String[] typeLevelPatterns = mapping.value();

            if (typeLevelPatterns.length > 0) { // url>0

                // 獲取controller中全部方法及方法的映射url

                String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);

                for (String typeLevelPattern : typeLevelPatterns) {

                    if (!typeLevelPattern.startsWith("/")) {

                        typeLevelPattern = "/" + typeLevelPattern;

                    }

                    boolean hasEmptyMethodLevelMappings = false;

                    for (String methodLevelPattern : methodLevelPatterns) {

                        if (methodLevelPattern == null) {

                            hasEmptyMethodLevelMappings = true;

                        } else {

                // controller的映射url+方法映射的url

                       String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);

                // 保存到set集合中                 addUrlsForPath(urls, combinedPattern);

                        }

                    }

                    if (hasEmptyMethodLevelMappings ||

                            org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {

                        addUrlsForPath(urls, typeLevelPattern);

                    }

                }

         // 以數組形式返回controller上的全部url

                return StringUtils.toStringArray(urls);

            }

            else {

                // controller上的@RequestMapping映射url爲空串,直接找方法的映射url

                return determineUrlsForHandlerMethods(handlerType, false);

            }

        // controller上沒@RequestMapping註解

        }  else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {

            // 獲取controller中方法上的映射url

            return determineUrlsForHandlerMethods(handlerType, false);

        }

        else {

            return null;

        }

}

上面的方式返回該controller中的全部url,

接下來把urls和controller放在handlerMap中

遍歷式存放for()循環


//具體存放的方法

protected void registerHandler(String urlPath, Object handler) {// Eagerly resolve handler if referencing singleton via name.

//若是經過名稱引用單體,則急於解析處理程序。(感受翻譯的不非常準確) if (!this.lazyInitHandlers && handler instanceof String) {if (getApplicationContext().isSingleton(handlerName)) {this.handlerMap.get(urlPath);

//若是存在重複的url,則報異常IllegalStateExceptionif (mappedHandler != null) {if (mappedHandler != resolvedHandler) { throw new IllegalStateException("Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");else {//不然url校驗if (urlPath.equals("/")) {  else if (urlPath.equals("/*")) { else {

//放到handlerMap中this.handlerMap.put(urlPath, resolvedHandler);

其中存放url和controller的map:

private final Map<String, Object> handlerMap new LinkedHashMap<String, Object>();

 

看完以上源碼我們能夠知道Map<urls,Controller>關係以及創建好了(HandlerMapping關係)

 

第二步:經過請求的Url查找須要訪問的controller

前面我們已經講過了DispatcherServlet爲核心代碼(入口),輕重黑心的方法爲doService()

,另外doService()中的核心邏輯由doDispatch()實現,咱們查看doDispatch()的源代碼.

org.springframework.web.servlet.DispatcherServlet

/** * 中央控制器,控制請求的轉發  */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {null;int interceptorIndex = -1;try {boolean errorView = false;try {// 1.檢查是不是文件上傳的請求                processedRequest = checkMultipart(request);// 2.取得處理當前請求的controller,這裏也稱爲hanlder,處理器,第一個步驟的意義就在這裏體現了.這裏並非直接返    

controller,而是返回的HandlerExecutionChain請求處理器鏈對象,該對象封裝了handlerinterceptors.                mappedHandler = getHandler(processedRequest, false);// 若是handler爲空,則返回404                if (mappedHandler == null || mappedHandler.getHandler() == null) {return;//3. 獲取處理request的處理器適配器handler adapter                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 處理 last-modified 請求頭                String method = request.getMethod();boolean isGet = "GET".equals(method);if(isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());urlPathHelper.getRequestUri(request);if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;// 4.攔截器的預處理方法                HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();if (interceptors != null) {for (int i = 0; i < interceptors.length; i++) {if(!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {null);return;// 5.實際的處理器處理請求,返回結果視圖對象                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//結果視圖對象的處理                if (mv != null && !mv.hasView()) {// 6.攔截器的後處理方法                if (interceptors != null) {for (int i = interceptors.length - 1; i >= 0; i--) {catch (ModelAndViewDefiningException ex) {catch (Exception ex) {null ? mappedHandler.getHandler() : null);null);if (mv != null && !mv.wasCleared()) {if (errorView) {clearErrorRequestAttributes(request);else {/* logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName()                             "': assuming HandlerAdapter completed request handling");*/            }//請求成功響應以後的方法            triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);catch (Exception e){//            logger.error("errpr".e);        }

上面getHandler(processedRequest, false)方法:


本方法方法實際上就是從HandlerMapping中找到url和controller的對應關係.這也就是第一個步驟:創建Map<url,Controller>的意義.咱們知道,最終處理request的是controller中的方法,咱們如今只是知道了controller,還要進一步確認controller中處理request的方法.因爲下面的步驟和第三個步驟關係更加緊密,直接轉到第三個步驟.

 

第三步:反射調用請求處理的方法,並返回視圖結果

上面的doDispatch()方法中,第2步其實就是從第一個步驟中的Map<urls,beanName>中取得controller,而後通過攔截器的預處理方法,到最核心的部分--第5步調用controller的方法處理請求.

在第2步中咱們已經知道了處理request的Controller類,第5步就是要根據url肯定controller中處理請求的方法,而後經過反射獲取該方法上的註解和參數,解析方法和參數上的註解,最後反射調用方法獲取ModelAndView結果視圖。

由於上面採用註解url形式說明的,因此咱們這裏繼續以註解處理器適配器來講明,

第5步調用的就是AnnotationMethodHandlerAdapter的handle(),該方法中的核心邏輯由invokeHandlerMethod(request, response, handler)實現。

/**

 *獲取處理請求的方法,執行並返回結果視圖

*/

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {

      // 1.獲取方法解析器

      ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);

      // 2.解析request中的url,獲取處理request的方法

      Method handlerMethod = methodResolver.resolveHandlerMethod(request);

      // 3.方法調用器

ServletHandlerMethodInvoker methodInvoker=new ServletHandlerMethodInvoker(methodResolver);

        ServletWebRequest webRequest = new ServletWebRequest(request, response);

        ExtendedModelMap implicitModel = new BindingAwareModelMap();

      // 4.執行方法

    Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);

       // 5.封裝結果視圖

   ModelAndView mav =methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);

    methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);

   return mav;

 }

這一部分的核心就在2和4了.先看第2步,經過request找controller的處理方法.實際上就是拼接controller的url和方法的url,與request的url進行匹配,找到匹配的方法.

自此代碼就回到了我們的controller中的方法返回類型ModelAndView了

相關文章
相關標籤/搜索