SpringMVC中的DispatcherServlet結(jié)構(gòu)和初始化詳解
前言
SpringMVC中Spring容器的關(guān)系是通過(guò)監(jiān)聽(tīng)方式啟動(dòng)的。
那么Spring(或者說(shuō)SpringMVC)與Servlet的Web容器(如:Tomcat、jetty)的關(guān)系則是通過(guò)DispatcherServlet進(jìn)行關(guān)聯(lián)。
在Web容器看,DispatcherServlet就是同一普通的Servlet,從Spring看是與Web的關(guān)聯(lián),會(huì)將所有的請(qǐng)求轉(zhuǎn)發(fā)到該控制器。
1、DispatcherServlet結(jié)構(gòu)
從層級(jí)能看出其頂層實(shí)現(xiàn)了Servlet和ServletConfig接口,由于Web容器只認(rèn)識(shí)Servlet,而Spring只認(rèn)識(shí)Bean,所以DispatcherServlet既是請(qǐng)求轉(zhuǎn)發(fā)和返回視圖解析控制中心,更是web與Spring的適配器,從其父子結(jié)構(gòu)層級(jí)就能看出,慢慢的從Servlet適配到Bean的過(guò)程。
1)、GenericServlet
GenericServlet實(shí)現(xiàn)了Servlet、ServletConfig接口,也實(shí)現(xiàn)了所有未實(shí)現(xiàn)的方法。主要是config相關(guān),init和service方法。
2)、HttpServlet
HttpServlet主要定義了Http協(xié)議相關(guān)的請(qǐng)求方法,以及Last-Modified和If-Modified-Since相關(guān)參數(shù)的處理。
3)、HttpServletBean
HttpServletBean除了繼承自HttpServlet,還實(shí)現(xiàn)了EnvironmentCapable和EnvironmentAware接口,處理Spring Environment相關(guān),之前分析過(guò)。
4)、FrameworkServlet
FrameworkServlet實(shí)現(xiàn)了接口ApplicationContextAware,注入了ApplicationContext,以及contextConfigLocation配置。
5)、DispatcherServlet
DispatcherServlet作為整個(gè)MVC的控制器,那么請(qǐng)求該轉(zhuǎn)發(fā)到哪個(gè)Controller,返回的model會(huì)該使用哪個(gè)視圖返回(視圖解析器進(jìn)行解析)。
都需要在這里進(jìn)行完成,所以初始化時(shí)需要使用到Spring MVC的九大件。
初始化時(shí)使用了策略模式初始化九大件
2、DispatcherServlet初始化(九大件的加載和初始化)
web容器啟動(dòng)時(shí)會(huì)加載配置的DispatcherServlet,則會(huì)調(diào)用其init方法,在GenericServlet中實(shí)現(xiàn),如下:
@Override public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); }
設(shè)置了ServletConfig值,并且調(diào)用了自定義無(wú)參空方法init,在子類(lèi)HttpServletBean中實(shí)現(xiàn)。
public final void init() throws ServletException { // 解析init-param參數(shù),并封裝成ServletConfigPropertyValues PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { throw ex; } } // Let subclasses do whatever initialization they like. initServletBean(); }
在初始化方法之前,允許init-param通過(guò)addRequiredProperty方法添加到requiredProperties屬性中的配置,以Bean的形式進(jìn)行加載。主要的方法初始化方法是調(diào)用本類(lèi)的空方法initServletBean,在下層子類(lèi)FrameworkServlet中實(shí)現(xiàn)
@Override protected final void initServletBean() throws ServletException { // 省略日志打印和,try catch部分的代碼 this.webApplicationContext = initWebApplicationContext(); // 留給子類(lèi)實(shí)現(xiàn),子類(lèi)DispatcherServlet沒(méi)有進(jìn)行實(shí)現(xiàn) initFrameworkServlet(); }
protected WebApplicationContext initWebApplicationContext() { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // 確保Spring容器創(chuàng)建并且唯一,所以無(wú)論ContextLoaderListener是否啟動(dòng)和refresh wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { onRefresh(wac); } } if (this.publishContext) { // 將Spring的WebApplicationContext容器,設(shè)置到Web容器中 String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }
無(wú)論監(jiān)聽(tīng)是否啟動(dòng)Spring容器執(zhí)行refresh方法,這里都會(huì)進(jìn)行檢查啟動(dòng)。
并將啟動(dòng)的Spring容器,設(shè)置到web容器中,所以當(dāng)啟動(dòng)完成后兩個(gè)容器中就是你中有我,我中有你的狀態(tài)。
Spring MVC的初始化會(huì)同步鎖synchronized(onRefreshMonitor)保證下初始化,在子類(lèi)層級(jí)DispatcherServlet中完成:
@Override protected void onRefresh(ApplicationContext context) { initStrategies(context); }
使用策略模式初始化九大件,為后續(xù)請(qǐng)求調(diào)用,視圖解析等做準(zhǔn)備。
protected void initStrategies(ApplicationContext context) { // 文件上傳 initMultipartResolver(context); // i18n相關(guān)解析器 initLocaleResolver(context); // 主題解析器(一種主題就是一類(lèi)網(wǎng)頁(yè)風(fēng)格) initThemeResolver(context); // 請(qǐng)求映射 initHandlerMappings(context); // 適配器,后面請(qǐng)求轉(zhuǎn)發(fā)的Controller會(huì)專(zhuān)門(mén)分析 initHandlerAdapters(context); // 操作異常的解析處理(中途發(fā)送異常該調(diào)整到哪里) initHandlerExceptionResolvers(context); // 請(qǐng)求的Controller沒(méi)有返回View時(shí),該如何解析 initRequestToViewNameTranslator(context); // 視圖解析器(ModelAndView中的view字符串怎么進(jìn)行解析) initViewResolvers(context); // 請(qǐng)求屬性(參數(shù))管理器,主要用于重定向等情況時(shí)獲取屬性,比如post重定向到get initFlashMapManager(context); }
九大件的處理方式大致相同,只是定義的Bean名稱(chēng)和調(diào)用getBean的類(lèi)型不同而已,比如上傳文件,如下:
private void initMultipartResolver(ApplicationContext context) { // 省略日志打印和try catch的代碼 this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); }
主要看初始化的什么類(lèi)型,是在靜態(tài)代碼塊的策略模式中加載初始化(DispatcherServlet.properties),如下:
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\ org.springframework.web.servlet.function.support.RouterFunctionMapping org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\ org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\ org.springframework.web.servlet.function.support.HandlerFunctionAdapter org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
到此這篇關(guān)于SpringMVC中的DispatcherServlet結(jié)構(gòu)和初始化詳解的文章就介紹到這了,更多相關(guān)DispatcherServlet結(jié)構(gòu)和初始化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java使用Optional實(shí)現(xiàn)優(yōu)雅避免空指針異常
空指針異常(NullPointerException)可以說(shuō)是Java程序員最容易遇到的問(wèn)題了。為了解決這個(gè)問(wèn)題,Java?8?版本中推出了?Optional?類(lèi),本文就來(lái)講講如何使用Optional實(shí)現(xiàn)優(yōu)雅避免空指針異常吧2023-03-03SpringBoot Web開(kāi)發(fā)之系統(tǒng)任務(wù)啟動(dòng)與路徑映射和框架整合
這篇文章主要介紹了SpringBoot Web開(kāi)發(fā)中的系統(tǒng)任務(wù)啟動(dòng)與路徑映射和Servlet、Filter、Listener框架整合,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08java信號(hào)量控制線(xiàn)程打印順序的示例分享
這篇文章主要介紹了java信號(hào)量控制線(xiàn)程打印順序的示例,如ABCABC這樣輸出線(xiàn)程,大家參考使用吧2014-01-01一個(gè)注解搞定Spring Security基于Oauth2的SSO單點(diǎn)登錄功能
本文主要介紹 同域 和 跨域 兩種不同場(chǎng)景單點(diǎn)登錄的實(shí)現(xiàn)原理,并使用 Spring Security 來(lái)實(shí)現(xiàn)一個(gè)最簡(jiǎn)單的跨域 SSO客戶(hù)端。對(duì)Spring Security基于Oauth2的SSO單點(diǎn)登錄功能感興趣的朋友一起看看吧2021-09-09Java8函數(shù)式接口的基礎(chǔ)學(xué)習(xí)教程
這篇文章主要給大家介紹了關(guān)于Java8函數(shù)式接口基礎(chǔ)學(xué)習(xí)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Jpa?Specification如何實(shí)現(xiàn)and和or同時(shí)使用查詢(xún)
這篇文章主要介紹了Jpa?Specification如何實(shí)現(xiàn)and和or同時(shí)使用查詢(xún),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11解決Spring Security中AuthenticationEntryPoint不生效相關(guān)問(wèn)題
這篇文章主要介紹了解決Spring Security中AuthenticationEntryPoint不生效相關(guān)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12SpringBoot項(xiàng)目啟動(dòng)執(zhí)行任務(wù)的多種方法小結(jié)
這篇文章主要介紹了SpringBoot項(xiàng)目啟動(dòng)執(zhí)行任務(wù)的多種方法小結(jié),本文給大家分享的這幾種方法經(jīng)常會(huì)被用到,當(dāng)我們的項(xiàng)目啟動(dòng)后需要調(diào)用對(duì)應(yīng)的方法,用來(lái)項(xiàng)目的初始化等,本文通過(guò)示例代碼講解的非常詳細(xì),需要的朋友參考下吧2023-07-07