【SpringMVC原始碼剖析】 前奏:夢開始的地方
阿新 • • 發佈:2019-02-06
SpringMVC原始碼剖析
縱覽:SpringMVC處理請求
本節作為SpringMVC原始碼剖析的基本,主要介紹以下三方面內容。
- 1.核心DispatcherServlet的載入方式
- 2.DispatcherServlet初始化過程
- 3.SpringMVC接收請求並作出響應(粗粒度)
1.核心DispatcherServlet的載入方式
- 1.1 傳統的JavaWeb工程,以web.xml來配置載入Servlet容器的上下文,這顯然有些麻煩。
- 1.2 自從Servlet3.0+的出現,Spring-web模組提供了一種免配置檔案,在如Tomcat(支援Servlet3.0+)這樣的Servlet容器啟動時,自動呼叫了實現的WebApplicationInitializer(全路徑org.springframework.web.WebApplicationInitializer)介面的類的onStartup方法,並將容器的ServletContext往下傳遞。
public interface WebApplicationInitializer {
void onStartup(ServletContext servletContext) throws ServletException;
}
- 1.3 從Spring-web模組的API中,找到子類AbstractAnnotationConfigDispatcherServletInitializer,它間接實現了WebApplicationInitializer介面,父類onStartup方法,首先建立RootWebApplicationContext並設定ContextLoaderListner監聽器;其次,註冊往servletContext註冊DispatcherServlet例項。
public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
registerDispatcherServlet(servletContext);
}
protected void registerDispatcherServlet(ServletContext servletContext) {
//此處往servletContext新增DispatcherServlet例項
//併為DispatcherServlet新增過濾器
}
- 1.4 由於AbstractAnnotationConfigDispatcherServletInitializer是抽象類,Spring容器不能注入。它重寫了父類建立根ApplicationContext與ServletApplicationContext方法,並抽象出兩個方法(用來建立根ApplicationContext和ServletApplicationContext容器時,需要注入的元件字類節陣列)。
public abstract class AbstractAnnotationConfigDispatcherServletInitializer
extends AbstractDispatcherServletInitializer {
@Override
protected WebApplicationContext createRootApplicationContext() {
...
}
@Override
protected WebApplicationContext createServletApplicationContext() {
...
}
//用來建立根ApplicationContext和ServletApplicationContext容器時,需要注入的元件字類節陣列
protected abstract Class<?>[] getRootConfigClasses();
protected abstract Class<?>[] getServletConfigClasses();
}
- 1.5 因此 我們只需要編寫SpringMVCInitializer來繼承AbstractAnnotationConfigDispatcherServletInitializer類,讓它來幫我們完成對DispatcherServlet例項的載入即可。
2.DispatcherServlet初始化過程
- 2.1 DispatcherServlet有個靜態塊,用於載入同包下DispatcherServlet.properties策略檔案的內容,程式碼如下
static {
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
}
}
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.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
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
- 2.2 DispatcherServlet作為一個HttpServlet類,在載入時,會呼叫init方法,查詢或建立WebApplication容器並重新整理操作(如果使用WebApplicationInitilazer載入方式,RootWebApplicationContext是已經存在了)
public abstract class HttpServletBean extends HttpServlet
implements EnvironmentCapable, EnvironmentAware {
@Override
public final void init() throws ServletException {
initServletBean();
}
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected final void initServletBean() throws ServletException {
this.webApplicationContext = initWebApplicationContext();
}
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()) {
...
//重新整理,呼叫cwac.refresh()方法
//觸發了ContextRefreshListener.onApplicationEvent方法
//呼叫FrameworkServlet.this.onApplicationEvent方法
//呼叫子類,即DispatcherServlet.onRefresh方法
configureAndRefreshWebApplicationContext(cwac);
}
}
}
...
return wac;
}
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
onRefresh(event.getApplicationContext());
}
protected void onRefresh(ApplicationContext context) {
// 呼叫子類,即DispatcherServlet.onRefresh方法
}
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
- 2.3 呼叫DispatcherServlet.onRefresh方法,
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//判斷Spring容器是否存在MultipartResolver,LocaleResolver,ThemeResolver,不存在列印日誌
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//預設行為,載入容器的hadlerMappings,handlerAdapters,handlerExceptionResolvers,並排序
//不存在則載入策略檔案
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
//容器中載入名為viewNameTranslator的解析器,
//如果找不到,從策略檔案載入第一個Translator,即
//org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
initRequestToViewNameTranslator(context);
/預設行為,載入容器的viewResolvers,並排序
//不存在則載入策略檔案
initViewResolvers(context);
//容器中載入名為flashMapManager,
//不存在的話,同上處理,找策略檔案中第一個
initFlashMapManager(context);
}
3.SpringMVC接收請求並作出響應(粗粒度)
- 3.1 DispatcherServlet作為httpServlet,當接收到請求時,會呼叫doService方法
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//往request設定setAttribute,將當前環境(本地化解析器,主題解析器等)給request,方法mvc框架其他元件可以獲取到。
...
try {
//真實呼叫
doDispatch(request, response);
}
finally {
//為request設定WebAsyncManager屬性
//如果是同步請求,恢復請求前的屬性
...
}
}
- 3.2 DispatcherServlet的doDistpach方法,才是接收請求的核心方法
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 {
//判斷request頭部ContentType是否以"multipart/"開頭,來標記是否為附件上傳
processedRequest = checkMultipart(request);
//是否附件請求標記
multipartRequestParsed = (processedRequest != request);
//
// 從handlerMappings(RequestMappingHanderMapping,策略檔案的handlerMapping類已經過時了),獲取HandlerExecutionChain
//HandlerExecutionChain(包含一個handlerMethod,以及一堆handlerInterceptor)
mappedHandler = getHandler(processedRequest);
//找不到handlerMethod時,根據標是否丟擲異常,還是直接response.sendError
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根據handerMethod找到HandlerAdapter(RequestMappingHandlerAdapter)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 修改 last-modified 頭部,如果支援的話.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 呼叫所有HandlerInterceptor的preHandle()方法,如果返回false,不處理請求
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 核心:::::通過RequestMappingHandlerAdapter的handle方法,處理請求邏輯,返回ModelView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//從viewNameTranslator中獲取預設檢視名,並設定到mv中
applyDefaultViewName(request, mv);
//呼叫所有HandlerInterceptor的postHandle()方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//渲染請求結果
//從ModelView中獲取View
//view.render(model,req,resp)渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//呼叫handerExecutionChain的afterCompletion
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
//呼叫handerExecutionChain的afterCompletion
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// 呼叫handerExecutionChain的applyAfterConcurrentHandlingStarted,實現非同步攔截
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 如果為附件請求,清理現場
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
注:從DispatcherServlet.properties知道預設的HandlerMapping實現類已經註解了@Deprecated,從註釋頭部得知,建議使用RequestMappingHandlerMapping類。Spring-Webmvc模組中,提供了@EnableWebMvc,它匯入了@Import(DelegatingWebMvcConfiguration.class)DelegatingWebMvcConfiguration類,而DelegatingWebMvcConfiguration類繼承了WebMvcConfigurationSupport,WebMvcConfigurationSupport又注入了RequestMappingHandlerMapping例項,如下面程式碼。所以可以通過註解@EnableWebMvc,向容器注入RequestMappingHandlerMapping例項。
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
//注入內容協商管理器handlerMapping.setContentNegotiationManager(mvcContentNegotiationManager());
PathMatchConfigurer configurer = getPathMatchConfigurer();
if (configurer.isUseSuffixPatternMatch() != null) {
handlerMapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
}
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
handlerMapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
}
if (configurer.isUseTrailingSlashMatch() != null) {
handlerMapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
}
if (configurer.getPathMatcher() != null) {
handlerMapping.setPathMatcher(configurer.getPathMatcher());
}
if (configurer.getUrlPathHelper() != null) {
handlerMapping.setUrlPathHelper(configurer.getUrlPathHelper());
}
return handlerMapping;
}
注:RequestMappingHandlerMapping間接實現了InitializingBean介面,重寫afterPropertiesSet,程式碼如下
@Override
public void afterPropertiesSet() {
if (this.useRegisteredSuffixPatternMatch) {
this.fileExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
}
//呼叫父類
super.afterPropertiesSet();
}
@Override
public void afterPropertiesSet() {
initHandlerMethods();//初始化handlerMethods
}
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
//收集所有註解了@Controller或@RequestMapping的方法
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
下節將細化介紹DispatcherServlet的getHandler(HttpServletRequest request)方法為入口進行原始碼剖析,將瞭解到
- 如何獲取到handlerMethod
- 如何獲取handlerInterceptor
- 如何建立handlerExecutionChain