SpringMVC實現原理解析

一、SpringMVC簡介

SpringMVC是一種基於Spring實現了Web MVC設計模式的請求驅動類型的輕量級Web框架,使用了MVC架構模式的思想,將web層進行職責解耦,並管理應用所需對象的生命週期,爲簡化日常開發,提供了很大便利。

二、SpringMVC核心組件

DispatcherServlet:中央控制器,統一調度其他組件的調用,是整個請求響應的控制中心,本質是一個Servlet;

Handler:業務處理器,處理客戶端的具體請求和返回處理結果,通常存在形式就是各種Controller;

HandlerMapping:處理器映射器,客戶端請求URL和業務處理器的映射關係,根據請求URL可以找到對應的業務處理器;

HandlerAdapter:處理器適配器,負責調用業務處理器的具體方法,返回邏輯視圖ModelAndView對象;

ViewResolver:視圖解析器,負責將業務處理器返回的視圖ModelAndView對象解析成JSP;

三、SpringMVC工作流程

1、客戶端發送請求,所有請求都有中央處理器DispatcherServlet處理;

2、DispatcherServlet通過處理器映射器HandlerMapping根據客戶端請求URL獲取對應的業務處理器Handler對象;

3、DispatcherServlet調用HandlerAdapter處理器適配器,通知HandlerAdapter執行具體哪個Handler;

4、HandlerAdapter調用具體Handler(Controller)的方法並得到返回的結果ModelAndView,且將結果返回給DispatcherServlet;

5、DispatcherServlet將ModelAndView交給ViewReslover視圖解析器解析,然後返回真正的視圖;

6、DispatcherServlet將模型數據填充到視圖中;

7、DispatcherServlet將結果響應給用戶。

四、SpringMVC流程圖

 

 

五、SpringMVC源碼解析

5.1、SpringMVC啓動流程

SpringMVC首先需要從web.xml配置DispatcherServlet,如下:

<servlet>
     <servlet-name>springmvc</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
      <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:springmvc.xml</param-value>
      </init-param>
</servlet>
<servlet-mapping>
     <servlet-name>springmvc</servlet-name>
     <url-pattern>/</url-pattern>
</servlet-mapping>

 

根據servlet相關知識可知所有請求都會交給DispatcherServlet處理,並且項目啓動時會創建DispatcherServlet並會執行DispatcherServlet的初始化init方法。

DispatcherServlet繼承之FrameworkServlet,FrameworkServlet繼承之HttpServletBean,HttpServletBean實現了HttpServlet的init方法,實際是執行了initServletBean方法,該方法被子類FrameworkServlet重寫,

FrameworkServlet重寫initServletBean方法實現邏輯如下:

 1 protected final void initServletBean() throws ServletException {
 2         try {
 3             /** 1.初始化Spring Web容器*/
 4             this.webApplicationContext = initWebApplicationContext();
 5             /** 2.初始化框架Servlet,空方法,交給子類擴展*/
 6             initFrameworkServlet();
 7         }
 8         catch (ServletException | RuntimeException ex) {
 9             logger.error("Context initialization failed", ex);
10             throw ex;
11         }
12     }

 

protected WebApplicationContext initWebApplicationContext() {

        /** 1. 嘗試獲取WebApplicationContext */
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            wac = findWebApplicationContext();
        }
        /** 2.如果當前沒有WebApplicationContext就初始化並刷新WebApplicationContext */
        if (wac == null) {
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            synchronized (this.onRefreshMonitor) {
                /** 3.WebApplicationContext初始化並刷新後,執行onRefresh方法*/
                onRefresh(wac);
            }
        }

        if (this.publishContext) {
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }
        return wac;
    }

 

通過createWebApplicationContext方法創建IOC容器WebApplicationContext並啓動刷新容器,當Spring容器啓動後再執行onRefresh方法刷新Servlet,Spring容器啓動刷新邏輯不再細看,onRefresh方法實際是交給了

子類DispatcherServlet實現,DispatcherServlet的onRefresh方法源碼如下:

/** DispatcherServlet onRefresh方法 */
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        /** 初始化處理器映射器HandlerMapping */
        initHandlerMappings(context);
        /** 初始化處理器適配器handlerAdapter */
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        /** 初始化視圖解析器ViewResolver */
        initViewResolvers(context);
        initFlashMapManager(context);
    }

可以看出onRefresh方法主要是初始化相關組件,如初始化業務處理器映射器HandlerMapping、處理器適配器HandlerAdapter、視圖解析器ViewResolver等,這裏着重分析HandlerMapping和HandlerAdapter的初始化。

5.1.1、處理器映射器初始化

首先看處理器映射器的初始化,方法爲DispatcherServlet的initHandlerMapping(ApplicationContext context), 源碼如下:

 1 /** 初始化處理器映射器 */
 2     private void initHandlerMappings(ApplicationContext context) {
 3         this.handlerMappings = null;
 4         /** 1.先嚐試從Spring容器中獲取所有HandlerMapping s*/
 5         if (this.detectAllHandlerMappings) {
 6             Map<String, HandlerMapping> matchingBeans =
 7                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
 8             if (!matchingBeans.isEmpty()) {
 9                 this.handlerMappings = new ArrayList<>(matchingBeans.values());
10                 AnnotationAwareOrderComparator.sort(this.handlerMappings);
11             }
12         }
13         else {
14             try {
15                 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
16                 this.handlerMappings = Collections.singletonList(hm);
17             }
18             catch (NoSuchBeanDefinitionException ex) {
19             }
20         }
21 
22         /** 2.如果Spring容器中沒有HandlerMapping,那麼就初始化默認的HandlerMapping*/
23         if (this.handlerMappings == null) {
24             // 初始化默認HandlerMapping
25             this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
26         }
27     }
28 
29     /** 獲取默認策略 */
30     protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
31         String key = strategyInterface.getName();
32         /** 1.從配置文件中獲取默認策略
33          *  配置文件爲DispatcherServlet.properties
34          *  key爲策略類全路徑
35          * */
36         String value = defaultStrategies.getProperty(key);
37         if (value != null) {
38             String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
39             List<T> strategies = new ArrayList<>(classNames.length);
40             for (String className : classNames) {
41                 try {
42                     /** 2.反射初始化所有策略實例*/
43                     Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
44                     Object strategy = createDefaultStrategy(context, clazz);
45                     strategies.add((T) strategy);
46                 }
47                 catch (ClassNotFoundException ex) {
48                 }
49                 catch (LinkageError err) {
50                 }
51             }
52             return strategies;
53         }
54         else {
55             return new LinkedList<>();
56         }
57     }

 

首先嚐試從Spring容器中獲取所有HandlerMapping的bean,如果不存在就加載默認處理器映射器,getDefaultStrategies方法是從配置文件DispatcherServlet.properties中加載默認配置,配置文件內容如下:

 1 org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
 2 
 3 org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
 4 
 5 org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
 6     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
 7     org.springframework.web.servlet.function.support.RouterFunctionMapping
 8 
 9 org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
10     org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
11     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
12     org.springframework.web.servlet.function.support.HandlerFunctionAdapter
13 
14 
15 org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
16     org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
17     org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
18 
19 org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
20 
21 org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
22 
23 org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

 

可以發現默認HandlerMapping爲BeanNameUrlHandlerMapping、RequestMappingHandlerMapping和RouterFunctionMapping,以RequestMappingHandlerMapping爲例,實現了InitializingBean,

所以初始化後會執行afterPropertiesSet方法,該方法初始化屬性,並調用父類AbstractHandlerMapping的afterPropertiesSet方法,該方法又執行了方法initHandlerMethods方法,AbstractHandlerMapping的

initHandlerMethods方法實質就是初始化方法映射的方法,邏輯如下:

/** 初始化路徑和方法映射 */
    protected void initHandlerMethods() {

        /** 1.遍歷Spring容器中所有的bean */
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                /** 2.處理所有後續的bean */
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            /** 1.獲取bean的Class對象 */
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
        }
        /** 2.調用isHandler方法判斷該bean是否是業務處理器*/
        if (beanType != null && isHandler(beanType)) {
            /** 2.尋找業務處理器中映射方法 */
            detectHandlerMethods(beanName);
        }
    }

    /** 判斷bean是否是業務處理器( 被Controller或RequestMapping註解修飾 )*/
    protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

 

遍歷Spring容器中所有的bean,判斷bean中是否包含@Controller或@RequestMapping註解,如果包含那麼就是業務處理器,那麼就執行detectHandlerMethods方法處理,該方法邏輯如下:

protected void detectHandlerMethods(Object handler) {
        /** 1.獲取處理器Class對象 */
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            /** 2.尋找所有映射方法集合,存入Map<Method,RequestMappingInfo>中
             *  RequestMappingInfo就是方法映射關係類 */
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                /** 註冊處理器、方法、映射關係, 緩存在MappingRegistry實例的Map中 */
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }

 

先從業務處理器中尋找所有映射方法封裝成映射關係實例,然後將處理器、方法和映射關係實例註冊到MappingRegistry實例中。

5.1.2、處理器適配器初始化

處理器適配器HandlerAdapter的初始化過程和處理器映射器HandlerMapping的初始化過程如出一轍,最終落實到默認的適配器RequestMappingHandlerAdapter的初始化,最終是重寫了afterPropertiesSet方法。

 

5.2、SpringMVC工作流程

 

 

------------恢復內容開始------------

一、SpringMVC簡介

SpringMVC是一種基於Spring實現了Web MVC設計模式的請求驅動類型的輕量級Web框架,使用了MVC架構模式的思想,將web層進行職責解耦,並管理應用所需對象的生命週期,爲簡化日常開發,提供了很大便利。

二、SpringMVC核心組件

DispatcherServlet:中央控制器,統一調度其他組件的調用,是整個請求響應的控制中心,本質是一個Servlet;

Handler:業務處理器,處理客戶端的具體請求和返回處理結果,通常存在形式就是各種Controller;

HandlerMapping:處理器映射器,客戶端請求URL和業務處理器的映射關係,根據請求URL可以找到對應的業務處理器;

HandlerAdapter:處理器適配器,負責調用業務處理器的具體方法,返回邏輯視圖ModelAndView對象;

ViewResolver:視圖解析器,負責將業務處理器返回的視圖ModelAndView對象解析成JSP;

三、SpringMVC工作流程

1、客戶端發送請求,所有請求都有中央處理器DispatcherServlet處理;

2、DispatcherServlet通過處理器映射器HandlerMapping根據客戶端請求URL獲取對應的業務處理器Handler對象;

3、DispatcherServlet調用HandlerAdapter處理器適配器,通知HandlerAdapter執行具體哪個Handler;

4、HandlerAdapter調用具體Handler(Controller)的方法並得到返回的結果ModelAndView,且將結果返回給DispatcherServlet;

5、DispatcherServlet將ModelAndView交給ViewReslover視圖解析器解析,然後返回真正的視圖;

6、DispatcherServlet將模型數據填充到視圖中;

7、DispatcherServlet將結果響應給用戶。

四、SpringMVC流程圖

 

 

五、SpringMVC源碼解析

5.1、SpringMVC啓動流程

SpringMVC首先需要從web.xml配置DispatcherServlet,如下:

<servlet>
     <servlet-name>springmvc</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
      <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:springmvc.xml</param-value>
      </init-param>
</servlet>
<servlet-mapping>
     <servlet-name>springmvc</servlet-name>
     <url-pattern>/</url-pattern>
</servlet-mapping>

 

根據servlet相關知識可知所有請求都會交給DispatcherServlet處理,並且項目啓動時會創建DispatcherServlet並會執行DispatcherServlet的初始化init方法。

DispatcherServlet繼承之FrameworkServlet,FrameworkServlet繼承之HttpServletBean,HttpServletBean實現了HttpServlet的init方法,實際是執行了initServletBean方法,該方法被子類FrameworkServlet重寫,

FrameworkServlet重寫initServletBean方法實現邏輯如下:

 1 protected final void initServletBean() throws ServletException {
 2         try {
 3             /** 1.初始化Spring Web容器*/
 4             this.webApplicationContext = initWebApplicationContext();
 5             /** 2.初始化框架Servlet,空方法,交給子類擴展*/
 6             initFrameworkServlet();
 7         }
 8         catch (ServletException | RuntimeException ex) {
 9             logger.error("Context initialization failed", ex);
10             throw ex;
11         }
12     }

 

protected WebApplicationContext initWebApplicationContext() {

        /** 1. 嘗試獲取WebApplicationContext */
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            wac = findWebApplicationContext();
        }
        /** 2.如果當前沒有WebApplicationContext就初始化並刷新WebApplicationContext */
        if (wac == null) {
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            synchronized (this.onRefreshMonitor) {
                /** 3.WebApplicationContext初始化並刷新後,執行onRefresh方法*/
                onRefresh(wac);
            }
        }

        if (this.publishContext) {
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }
        return wac;
    }

 

通過createWebApplicationContext方法創建IOC容器WebApplicationContext並啓動刷新容器,當Spring容器啓動後再執行onRefresh方法刷新Servlet,Spring容器啓動刷新邏輯不再細看,onRefresh方法實際是交給了

子類DispatcherServlet實現,DispatcherServlet的onRefresh方法源碼如下:

/** DispatcherServlet onRefresh方法 */
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        /** 初始化處理器映射器HandlerMapping */
        initHandlerMappings(context);
        /** 初始化處理器適配器handlerAdapter */
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        /** 初始化視圖解析器ViewResolver */
        initViewResolvers(context);
        initFlashMapManager(context);
    }

可以看出onRefresh方法主要是初始化相關組件,如初始化業務處理器映射器HandlerMapping、處理器適配器HandlerAdapter、視圖解析器ViewResolver等,這裏着重分析HandlerMapping和HandlerAdapter的初始化。

5.1.1、處理器映射器初始化

首先看處理器映射器的初始化,方法爲DispatcherServlet的initHandlerMapping(ApplicationContext context), 源碼如下:

 1 /** 初始化處理器映射器 */
 2     private void initHandlerMappings(ApplicationContext context) {
 3         this.handlerMappings = null;
 4         /** 1.先嚐試從Spring容器中獲取所有HandlerMapping s*/
 5         if (this.detectAllHandlerMappings) {
 6             Map<String, HandlerMapping> matchingBeans =
 7                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
 8             if (!matchingBeans.isEmpty()) {
 9                 this.handlerMappings = new ArrayList<>(matchingBeans.values());
10                 AnnotationAwareOrderComparator.sort(this.handlerMappings);
11             }
12         }
13         else {
14             try {
15                 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
16                 this.handlerMappings = Collections.singletonList(hm);
17             }
18             catch (NoSuchBeanDefinitionException ex) {
19             }
20         }
21 
22         /** 2.如果Spring容器中沒有HandlerMapping,那麼就初始化默認的HandlerMapping*/
23         if (this.handlerMappings == null) {
24             // 初始化默認HandlerMapping
25             this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
26         }
27     }
28 
29     /** 獲取默認策略 */
30     protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
31         String key = strategyInterface.getName();
32         /** 1.從配置文件中獲取默認策略
33          *  配置文件爲DispatcherServlet.properties
34          *  key爲策略類全路徑
35          * */
36         String value = defaultStrategies.getProperty(key);
37         if (value != null) {
38             String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
39             List<T> strategies = new ArrayList<>(classNames.length);
40             for (String className : classNames) {
41                 try {
42                     /** 2.反射初始化所有策略實例*/
43                     Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
44                     Object strategy = createDefaultStrategy(context, clazz);
45                     strategies.add((T) strategy);
46                 }
47                 catch (ClassNotFoundException ex) {
48                 }
49                 catch (LinkageError err) {
50                 }
51             }
52             return strategies;
53         }
54         else {
55             return new LinkedList<>();
56         }
57     }

 

首先嚐試從Spring容器中獲取所有HandlerMapping的bean,如果不存在就加載默認處理器映射器,getDefaultStrategies方法是從配置文件DispatcherServlet.properties中加載默認配置,配置文件內容如下:

 1 org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
 2 
 3 org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
 4 
 5 org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
 6     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
 7     org.springframework.web.servlet.function.support.RouterFunctionMapping
 8 
 9 org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
10     org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
11     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
12     org.springframework.web.servlet.function.support.HandlerFunctionAdapter
13 
14 
15 org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
16     org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
17     org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
18 
19 org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
20 
21 org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
22 
23 org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

 

可以發現默認HandlerMapping爲BeanNameUrlHandlerMapping、RequestMappingHandlerMapping和RouterFunctionMapping,以RequestMappingHandlerMapping爲例,實現了InitializingBean,

所以初始化後會執行afterPropertiesSet方法,該方法初始化屬性,並調用父類AbstractHandlerMapping的afterPropertiesSet方法,該方法又執行了方法initHandlerMethods方法,AbstractHandlerMapping的

initHandlerMethods方法實質就是初始化方法映射的方法,邏輯如下:

/** 初始化路徑和方法映射 */
    protected void initHandlerMethods() {

        /** 1.遍歷Spring容器中所有的bean */
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                /** 2.處理所有後續的bean */
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            /** 1.獲取bean的Class對象 */
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
        }
        /** 2.調用isHandler方法判斷該bean是否是業務處理器*/
        if (beanType != null && isHandler(beanType)) {
            /** 2.尋找業務處理器中映射方法 */
            detectHandlerMethods(beanName);
        }
    }

    /** 判斷bean是否是業務處理器( 被Controller或RequestMapping註解修飾 )*/
    protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

 

遍歷Spring容器中所有的bean,判斷bean中是否包含@Controller或@RequestMapping註解,如果包含那麼就是業務處理器,那麼就執行detectHandlerMethods方法處理,該方法邏輯如下:

protected void detectHandlerMethods(Object handler) {
        /** 1.獲取處理器Class對象 */
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            /** 2.尋找所有映射方法集合,存入Map<Method,RequestMappingInfo>中
             *  RequestMappingInfo就是方法映射關係類 */
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                /** 註冊處理器、方法、映射關係, 緩存在MappingRegistry實例的Map中 */
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }

 

先從業務處理器中尋找所有映射方法封裝成映射關係實例,然後將處理器、方法和映射關係實例註冊到MappingRegistry實例中。

5.1.2、處理器適配器初始化

處理器適配器HandlerAdapter的初始化過程和處理器映射器HandlerMapping的初始化過程如出一轍,最終落實到默認的適配器RequestMappingHandlerAdapter的初始化,最終是重寫了afterPropertiesSet方法。

 

5.2、SpringMVC工作流程

 

 

------------恢復內容結束------------

------------恢復內容開始------------

一、SpringMVC簡介

SpringMVC是一種基於Spring實現了Web MVC設計模式的請求驅動類型的輕量級Web框架,使用了MVC架構模式的思想,將web層進行職責解耦,並管理應用所需對象的生命週期,爲簡化日常開發,提供了很大便利。

二、SpringMVC核心組件

DispatcherServlet:中央控制器,統一調度其他組件的調用,是整個請求響應的控制中心,本質是一個Servlet;

Handler:業務處理器,處理客戶端的具體請求和返回處理結果,通常存在形式就是各種Controller;

HandlerMapping:處理器映射器,客戶端請求URL和業務處理器的映射關係,根據請求URL可以找到對應的業務處理器;

HandlerAdapter:處理器適配器,負責調用業務處理器的具體方法,返回邏輯視圖ModelAndView對象;

ViewResolver:視圖解析器,負責將業務處理器返回的視圖ModelAndView對象解析成JSP;

三、SpringMVC工作流程

1、客戶端發送請求,所有請求都有中央處理器DispatcherServlet處理;

2、DispatcherServlet通過處理器映射器HandlerMapping根據客戶端請求URL獲取對應的業務處理器Handler對象;

3、DispatcherServlet調用HandlerAdapter處理器適配器,通知HandlerAdapter執行具體哪個Handler;

4、HandlerAdapter調用具體Handler(Controller)的方法並得到返回的結果ModelAndView,且將結果返回給DispatcherServlet;

5、DispatcherServlet將ModelAndView交給ViewReslover視圖解析器解析,然後返回真正的視圖;

6、DispatcherServlet將模型數據填充到視圖中;

7、DispatcherServlet將結果響應給用戶。

四、SpringMVC流程圖

 

 

五、SpringMVC源碼解析

5.1、SpringMVC啓動流程

SpringMVC首先需要從web.xml配置DispatcherServlet,如下:

<servlet>
     <servlet-name>springmvc</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
      <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:springmvc.xml</param-value>
      </init-param>
</servlet>
<servlet-mapping>
     <servlet-name>springmvc</servlet-name>
     <url-pattern>/</url-pattern>
</servlet-mapping>

 

根據servlet相關知識可知所有請求都會交給DispatcherServlet處理,並且項目啓動時會創建DispatcherServlet並會執行DispatcherServlet的初始化init方法。

DispatcherServlet繼承之FrameworkServlet,FrameworkServlet繼承之HttpServletBean,HttpServletBean實現了HttpServlet的init方法,實際是執行了initServletBean方法,該方法被子類FrameworkServlet重寫,

FrameworkServlet重寫initServletBean方法實現邏輯如下:

 1 protected final void initServletBean() throws ServletException {
 2         try {
 3             /** 1.初始化Spring Web容器*/
 4             this.webApplicationContext = initWebApplicationContext();
 5             /** 2.初始化框架Servlet,空方法,交給子類擴展*/
 6             initFrameworkServlet();
 7         }
 8         catch (ServletException | RuntimeException ex) {
 9             logger.error("Context initialization failed", ex);
10             throw ex;
11         }
12     }

 

protected WebApplicationContext initWebApplicationContext() {

        /** 1. 嘗試獲取WebApplicationContext */
        WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
        WebApplicationContext wac = null;
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
            if (wac instanceof ConfigurableWebApplicationContext) {
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
                if (!cwac.isActive()) {
                    if (cwac.getParent() == null) {
                        cwac.setParent(rootContext);
                    }
                    configureAndRefreshWebApplicationContext(cwac);
                }
            }
        }
        if (wac == null) {
            wac = findWebApplicationContext();
        }
        /** 2.如果當前沒有WebApplicationContext就初始化並刷新WebApplicationContext */
        if (wac == null) {
            wac = createWebApplicationContext(rootContext);
        }

        if (!this.refreshEventReceived) {
            synchronized (this.onRefreshMonitor) {
                /** 3.WebApplicationContext初始化並刷新後,執行onRefresh方法*/
                onRefresh(wac);
            }
        }

        if (this.publishContext) {
            String attrName = getServletContextAttributeName();
            getServletContext().setAttribute(attrName, wac);
        }
        return wac;
    }

 

通過createWebApplicationContext方法創建IOC容器WebApplicationContext並啓動刷新容器,當Spring容器啓動後再執行onRefresh方法刷新Servlet,Spring容器啓動刷新邏輯不再細看,onRefresh方法實際是交給了

子類DispatcherServlet實現,DispatcherServlet的onRefresh方法源碼如下:

/** DispatcherServlet onRefresh方法 */
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        initMultipartResolver(context);
        initLocaleResolver(context);
        initThemeResolver(context);
        /** 初始化處理器映射器HandlerMapping */
        initHandlerMappings(context);
        /** 初始化處理器適配器handlerAdapter */
        initHandlerAdapters(context);
        initHandlerExceptionResolvers(context);
        initRequestToViewNameTranslator(context);
        /** 初始化視圖解析器ViewResolver */
        initViewResolvers(context);
        initFlashMapManager(context);
    }

可以看出onRefresh方法主要是初始化相關組件,如初始化業務處理器映射器HandlerMapping、處理器適配器HandlerAdapter、視圖解析器ViewResolver等,這裏着重分析HandlerMapping和HandlerAdapter的初始化。

5.1.1、處理器映射器初始化

首先看處理器映射器的初始化,方法爲DispatcherServlet的initHandlerMapping(ApplicationContext context), 源碼如下:

 1 /** 初始化處理器映射器 */
 2     private void initHandlerMappings(ApplicationContext context) {
 3         this.handlerMappings = null;
 4         /** 1.先嚐試從Spring容器中獲取所有HandlerMapping s*/
 5         if (this.detectAllHandlerMappings) {
 6             Map<String, HandlerMapping> matchingBeans =
 7                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
 8             if (!matchingBeans.isEmpty()) {
 9                 this.handlerMappings = new ArrayList<>(matchingBeans.values());
10                 AnnotationAwareOrderComparator.sort(this.handlerMappings);
11             }
12         }
13         else {
14             try {
15                 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
16                 this.handlerMappings = Collections.singletonList(hm);
17             }
18             catch (NoSuchBeanDefinitionException ex) {
19             }
20         }
21 
22         /** 2.如果Spring容器中沒有HandlerMapping,那麼就初始化默認的HandlerMapping*/
23         if (this.handlerMappings == null) {
24             // 初始化默認HandlerMapping
25             this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
26         }
27     }
28 
29     /** 獲取默認策略 */
30     protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
31         String key = strategyInterface.getName();
32         /** 1.從配置文件中獲取默認策略
33          *  配置文件爲DispatcherServlet.properties
34          *  key爲策略類全路徑
35          * */
36         String value = defaultStrategies.getProperty(key);
37         if (value != null) {
38             String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
39             List<T> strategies = new ArrayList<>(classNames.length);
40             for (String className : classNames) {
41                 try {
42                     /** 2.反射初始化所有策略實例*/
43                     Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
44                     Object strategy = createDefaultStrategy(context, clazz);
45                     strategies.add((T) strategy);
46                 }
47                 catch (ClassNotFoundException ex) {
48                 }
49                 catch (LinkageError err) {
50                 }
51             }
52             return strategies;
53         }
54         else {
55             return new LinkedList<>();
56         }
57     }

 

首先嚐試從Spring容器中獲取所有HandlerMapping的bean,如果不存在就加載默認處理器映射器,getDefaultStrategies方法是從配置文件DispatcherServlet.properties中加載默認配置,配置文件內容如下:

 1 org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
 2 
 3 org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
 4 
 5 org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
 6     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
 7     org.springframework.web.servlet.function.support.RouterFunctionMapping
 8 
 9 org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
10     org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
11     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
12     org.springframework.web.servlet.function.support.HandlerFunctionAdapter
13 
14 
15 org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
16     org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
17     org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
18 
19 org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
20 
21 org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
22 
23 org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

 

可以發現默認HandlerMapping爲BeanNameUrlHandlerMapping、RequestMappingHandlerMapping和RouterFunctionMapping,以RequestMappingHandlerMapping爲例,實現了InitializingBean,

所以初始化後會執行afterPropertiesSet方法,該方法初始化屬性,並調用父類AbstractHandlerMapping的afterPropertiesSet方法,該方法又執行了方法initHandlerMethods方法,AbstractHandlerMapping的

initHandlerMethods方法實質就是初始化方法映射的方法,邏輯如下:

/** 初始化路徑和方法映射 */
    protected void initHandlerMethods() {

        /** 1.遍歷Spring容器中所有的bean */
        for (String beanName : getCandidateBeanNames()) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                /** 2.處理所有後續的bean */
                processCandidateBean(beanName);
            }
        }
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void processCandidateBean(String beanName) {
        Class<?> beanType = null;
        try {
            /** 1.獲取bean的Class對象 */
            beanType = obtainApplicationContext().getType(beanName);
        }
        catch (Throwable ex) {
        }
        /** 2.調用isHandler方法判斷該bean是否是業務處理器*/
        if (beanType != null && isHandler(beanType)) {
            /** 2.尋找業務處理器中映射方法 */
            detectHandlerMethods(beanName);
        }
    }

    /** 判斷bean是否是業務處理器( 被Controller或RequestMapping註解修飾 )*/
    protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

 

遍歷Spring容器中所有的bean,判斷bean中是否包含@Controller或@RequestMapping註解,如果包含那麼就是業務處理器,那麼就執行detectHandlerMethods方法處理,該方法邏輯如下:

protected void detectHandlerMethods(Object handler) {
        /** 1.獲取處理器Class對象 */
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            /** 2.尋找所有映射方法集合,存入Map<Method,RequestMappingInfo>中
             *  RequestMappingInfo就是方法映射關係類 */
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            return getMappingForMethod(method, userType);
                        }
                        catch (Throwable ex) {
                            throw new IllegalStateException("Invalid mapping on handler class [" +
                                    userType.getName() + "]: " + method, ex);
                        }
                    });
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                /** 註冊處理器、方法、映射關係, 緩存在MappingRegistry實例的Map中 */
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }

 

先從業務處理器中尋找所有映射方法封裝成映射關係實例,然後將處理器、方法和映射關係實例註冊到MappingRegistry實例中。

5.1.2、處理器適配器初始化

處理器適配器HandlerAdapter的初始化過程和處理器映射器HandlerMapping的初始化過程如出一轍,最終落實到默認的適配器RequestMappingHandlerAdapter的初始化,最終是重寫了afterPropertiesSet方法。 

5.2、SpringMVC工作流程

 Servlet工作流程實際就是接收到客戶端請求之後執行service方法,所以SpringMVC的請求處理入口就是DispatcherServlet的service方法,DispatcherServlet的service方法是執行了父類FrameworkServlet的service方法。

該方法又執行了FrameworkServlet的processRequest方法,最終調用了子類的doService方法,DispatcherServlet的doService方法就是處理業務邏輯的核心方法,源碼如下:

/** DispatcherServlet 處理業務方法 */
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        /** 1.請求參數快照,將請求參數緩存起來 */
        Map<String, Object> attributesSnapshot = null;
        if (WebUtils.isIncludeRequest(request)) {
            attributesSnapshot = new HashMap<>();
            Enumeration<?> attrNames = request.getAttributeNames();
            while (attrNames.hasMoreElements()) {
                String attrName = (String) attrNames.nextElement();
                if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
                    attributesSnapshot.put(attrName, request.getAttribute(attrName));
                }
            }
        }

        /** 2.請求參數添加配置 */
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
        request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
        request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
        request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

        if (this.flashMapManager != null) {
            FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
            if (inputFlashMap != null) {
                request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
            }
            request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
            request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
        }

        try {
            /** 3.執行分配請求處理 */
            doDispatch(request, response);
        }
        finally {
            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
                if (attributesSnapshot != null) {
                    restoreAttributesAfterInclude(request, attributesSnapshot);
                }
            }
        }
    }

 

首先進行參數處理,然後調用doDispatch方法分配請求,邏輯如下:

 

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                /** 1.根據請求從HandlerMapping中查詢具體的業務處理器 */
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }
                /** 2.根據業務處理器查詢對應業務處理器適配器 */
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }
                /** 3.調用處理器適配器的handle方法處理具體的業務邏輯 */
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
                applyDefaultViewName(processedRequest, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                //...
            }
            /** 4.處理請求執行結果 */
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        } catch (Exception ex) {
            //...
        } finally {
            //...
        }
    }

核心邏輯比較清晰,先從處理器映射器中查詢請求對應的業務處理器,然後再根據業務處理器找到處理器適配器,然後調用適配器的handle方法處理業務,最終執行processDispatchResult方法處理請求的處理結果。

getHandler邏輯就是從集合handlerMappings中找到匹配的處理器;

getHandlerAdapter就是從集合handlerAdapters中找到對應的適配器;

handle方法就是通過反射機制執行對應處理器的方法;

processDispatchResult就是將執行結果封裝成ModelAndView對象; 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章