一、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對象;