SpringMVC中的DispatcherServlet結構和初始化詳解
前言
SpringMVC中Spring容器的關系是通過監(jiān)聽方式啟動的。
那么Spring(或者說SpringMVC)與Servlet的Web容器(如:Tomcat、jetty)的關系則是通過DispatcherServlet進行關聯(lián)。
在Web容器看,DispatcherServlet就是同一普通的Servlet,從Spring看是與Web的關聯(lián),會將所有的請求轉發(fā)到該控制器。
1、DispatcherServlet結構

從層級能看出其頂層實現(xiàn)了Servlet和ServletConfig接口,由于Web容器只認識Servlet,而Spring只認識Bean,所以DispatcherServlet既是請求轉發(fā)和返回視圖解析控制中心,更是web與Spring的適配器,從其父子結構層級就能看出,慢慢的從Servlet適配到Bean的過程。
1)、GenericServlet
GenericServlet實現(xiàn)了Servlet、ServletConfig接口,也實現(xiàn)了所有未實現(xiàn)的方法。主要是config相關,init和service方法。
2)、HttpServlet
HttpServlet主要定義了Http協(xié)議相關的請求方法,以及Last-Modified和If-Modified-Since相關參數(shù)的處理。
3)、HttpServletBean
HttpServletBean除了繼承自HttpServlet,還實現(xiàn)了EnvironmentCapable和EnvironmentAware接口,處理Spring Environment相關,之前分析過。
4)、FrameworkServlet
FrameworkServlet實現(xiàn)了接口ApplicationContextAware,注入了ApplicationContext,以及contextConfigLocation配置。
5)、DispatcherServlet
DispatcherServlet作為整個MVC的控制器,那么請求該轉發(fā)到哪個Controller,返回的model會該使用哪個視圖返回(視圖解析器進行解析)。
都需要在這里進行完成,所以初始化時需要使用到Spring MVC的九大件。
初始化時使用了策略模式初始化九大件
2、DispatcherServlet初始化(九大件的加載和初始化)
web容器啟動時會加載配置的DispatcherServlet,則會調用其init方法,在GenericServlet中實現(xiàn),如下:
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}設置了ServletConfig值,并且調用了自定義無參空方法init,在子類HttpServletBean中實現(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通過addRequiredProperty方法添加到requiredProperties屬性中的配置,以Bean的形式進行加載。主要的方法初始化方法是調用本類的空方法initServletBean,在下層子類FrameworkServlet中實現(xiàn)
@Override
protected final void initServletBean() throws ServletException {
// 省略日志打印和,try catch部分的代碼
this.webApplicationContext = initWebApplicationContext();
// 留給子類實現(xiàn),子類DispatcherServlet沒有進行實現(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)建并且唯一,所以無論ContextLoaderListener是否啟動和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容器,設置到Web容器中
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}無論監(jiān)聽是否啟動Spring容器執(zhí)行refresh方法,這里都會進行檢查啟動。
并將啟動的Spring容器,設置到web容器中,所以當啟動完成后兩個容器中就是你中有我,我中有你的狀態(tài)。
Spring MVC的初始化會同步鎖synchronized(onRefreshMonitor)保證下初始化,在子類層級DispatcherServlet中完成:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}使用策略模式初始化九大件,為后續(xù)請求調用,視圖解析等做準備。
protected void initStrategies(ApplicationContext context) {
// 文件上傳
initMultipartResolver(context);
// i18n相關解析器
initLocaleResolver(context);
// 主題解析器(一種主題就是一類網(wǎng)頁風格)
initThemeResolver(context);
// 請求映射
initHandlerMappings(context);
// 適配器,后面請求轉發(fā)的Controller會專門分析
initHandlerAdapters(context);
// 操作異常的解析處理(中途發(fā)送異常該調整到哪里)
initHandlerExceptionResolvers(context);
// 請求的Controller沒有返回View時,該如何解析
initRequestToViewNameTranslator(context);
// 視圖解析器(ModelAndView中的view字符串怎么進行解析)
initViewResolvers(context);
// 請求屬性(參數(shù))管理器,主要用于重定向等情況時獲取屬性,比如post重定向到get
initFlashMapManager(context);
}九大件的處理方式大致相同,只是定義的Bean名稱和調用getBean的類型不同而已,比如上傳文件,如下:
private void initMultipartResolver(ApplicationContext context) {
// 省略日志打印和try catch的代碼
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
}主要看初始化的什么類型,是在靜態(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
到此這篇關于SpringMVC中的DispatcherServlet結構和初始化詳解的文章就介紹到這了,更多相關DispatcherServlet結構和初始化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java使用Optional實現(xiàn)優(yōu)雅避免空指針異常
空指針異常(NullPointerException)可以說是Java程序員最容易遇到的問題了。為了解決這個問題,Java?8?版本中推出了?Optional?類,本文就來講講如何使用Optional實現(xiàn)優(yōu)雅避免空指針異常吧2023-03-03
SpringBoot Web開發(fā)之系統(tǒng)任務啟動與路徑映射和框架整合
這篇文章主要介紹了SpringBoot Web開發(fā)中的系統(tǒng)任務啟動與路徑映射和Servlet、Filter、Listener框架整合,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-08-08
一個注解搞定Spring Security基于Oauth2的SSO單點登錄功能
本文主要介紹 同域 和 跨域 兩種不同場景單點登錄的實現(xiàn)原理,并使用 Spring Security 來實現(xiàn)一個最簡單的跨域 SSO客戶端。對Spring Security基于Oauth2的SSO單點登錄功能感興趣的朋友一起看看吧2021-09-09
Jpa?Specification如何實現(xiàn)and和or同時使用查詢
這篇文章主要介紹了Jpa?Specification如何實現(xiàn)and和or同時使用查詢,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
解決Spring Security中AuthenticationEntryPoint不生效相關問題
這篇文章主要介紹了解決Spring Security中AuthenticationEntryPoint不生效相關問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot項目啟動執(zhí)行任務的多種方法小結
這篇文章主要介紹了SpringBoot項目啟動執(zhí)行任務的多種方法小結,本文給大家分享的這幾種方法經(jīng)常會被用到,當我們的項目啟動后需要調用對應的方法,用來項目的初始化等,本文通過示例代碼講解的非常詳細,需要的朋友參考下吧2023-07-07

