SpringMVC核心就是DispatcherServlet,全部得請求都會轉發到DispatcherServlet,而後再經過DispatcherServlet執行具體得控制層(Handler)返回ModelAndView給客戶端視圖展現。java
DispatcherServlet其實就是一個Servlet類,無非就是包裝一層,經過url可以映射找到咱們得SpringMvc中定義得請求方法。ios
DispatcherServlet繼承FrameworkServlet繼承HttpServletweb
面向基本上思想 重寫 先走父類 ,在走子類。spring
得出答案:先看HttpServlet再找到咱們最後的子類服務器
當咱們第一次請求一個地址的時候,若是可以訪問,他會返回一個200表明訪問成功,此時應答頭信息中會有一個 Last-Modified 表明服務器這個文件的最後修改時間。app
當咱們再次請求的時候,若是請求過了,就會在請求頭,有一個If-Modified-Since的值,這時候傳到服務器,服務器就會拿這個值和上次修改的時間對比,若是小於上次修改的時間,說明服務器上的文件被修改過,就再次從服務器進行下載,返回200less
若是沒有修改就像上圖同樣,返回一個304,表明客戶端已經執行了GET,但文件未變化。async
既然是Servlet類,那麼他有一個最終的方法,就是service()方法,他是Servlet最核心的方法。ide
HttpServlet#servicepost
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if (method.equals(METHOD_GET)) { long lastModified = getLastModified(req); if (lastModified == -1) { // servlet doesn't support if-modified-since, no reason // to go through further expensive logic doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); if (ifModifiedSince < lastModified) { // If the servlet mod time is later, call doGet() // Round down to the nearest second for a proper compare // A ifModifiedSince of -1 will always be less maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } } } else if (method.equals(METHOD_HEAD)) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if (method.equals(METHOD_POST)) { doPost(req, resp); } else if (method.equals(METHOD_PUT)) { doPut(req, resp); } else if (method.equals(METHOD_DELETE)) { doDelete(req, resp); } else if (method.equals(METHOD_OPTIONS)) { doOptions(req,resp); } else if (method.equals(METHOD_TRACE)) { doTrace(req,resp); } else { // // Note that this means NO servlet supports whatever // method was requested, anywhere on this server. // String errMsg = lStrings.getString("http.method_not_implemented"); Object[] errArgs = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); } }
maybeSetLastModified(resp, lastModified);
/* * Sets the Last-Modified entity header field, if it has not * already been set and if the value is meaningful. Called before * doGet, to ensure that headers are set before response data is * written. A subclass might have set this header already, so we * check. */ private void maybeSetLastModified(HttpServletResponse resp, long lastModified) { if (resp.containsHeader(HEADER_LASTMOD)) return; if (lastModified >= 0) resp.setDateHeader(HEADER_LASTMOD, lastModified); }
所以,咱們在HttpServletBean類中找service方法,發現沒有,咱們繼續往上一層FrameworkServlet類中找,發現找到了,所以spring實現該方法在這個類去實現的。
/** * Override the parent class implementation in order to intercept PATCH requests. */ @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); } }
這裏職責主要是先拿到一個請求,而後又作了一個判斷請求方式。發現不是PATCH方式就去調用父類(HttpServlet)中service()方法。他去調用用父類中的service方法其實就是去調用該類中doPost(),doGet()方法,拿到不一樣的請求方式而後處理不一樣的業務。好比以FrameworkServlet的get方式爲例:
@Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); }
FrameWorkServlet#processRequest
這個方法裏面能夠直接看到this.doService(request, response);方法。在debug期間,進去該方法,發現這個方法直接跳到DispatcherServlet 類中,由上可知,這個方法就像一直被子類重寫。
/** * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch} * for the actual dispatching. */ @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { ... try { doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. 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); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. 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; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
首先主要是建立一個視圖對象 ModelAndView mv = null;而後檢查當前請求是不是二進制的請求processedRequest = this.checkMultipart(request);而後就是隻要代碼
mappedHandler = this.getHandler(processedRequest);
就是根據當前的請求去拿一個Handler.(在這個源碼中springMVC都是使用的Handler,那麼他到時是什麼?這個Handler其實就是咱們的控制器,包括咱們寫Controller)。進入這個方法源碼以下:
/** * Return the HandlerExecutionChain for this request. * <p>Tries all handler mappings in order. * @param request current HTTP request * @return the HandlerExecutionChain, or {@code null} if no handler could be found */ @Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
由流程圖可知,發送清求到控制器,控制器第二個節點就是發送第二個請求就是去拿Handler,所以可知這裏纔是最核心代碼。由圖可知他取Handler最終要去找HandlerMapping,而後他再去拿一個Handler。那麼爲何要去找HandlerMapping去要一個Handler呢?
首先咱們在配置控制器的時候有兩種方式1.xml方式,2.註解的方式。所以spring源碼他給咱們不止一種控制器 。由於兩種方式控制器 。所以spring並不知道咱們使用的是哪種控制器。由於兩種控制器,spring去底層去找的控制的實現方式是不同的。所以這就是爲何第二步他要去找Handler(控制器)的了。可是Handler怎麼找的到呢?就是經過HandlerMapping這樣一個處理器映射器。
Handler分裝了咱們建立的Controller和一個攔截器。
所以到這裏咱們就拿到了對應的也是最合適的Handler,而後返回中央處理器。
第二個方法:
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
獲取控制器的適配器。也就是咱們以前拿到了控制器,接下來要去執行控制器,也就是拿到控制器適配器中執行控制器。這裏爲何要獲取適配器呢?由於跟控制器映射器(也就是配置方式)同樣。
接下來判斷你有沒有須要執行的攔截器。:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}
/** * Apply preHandle methods of registered interceptors. * @return {@code true} if the execution chain should proceed with the * next interceptor or the handler itself. Else, DispatcherServlet assumes * that this interceptor has already dealt with the response itself. */ boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; }
適配器去執行Handler
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
若是你有ModelAndView,就返回一個ModelAndView.而後返回給試圖對象,而後把視圖對象交給視圖解析器,去渲染,最後響應給用戶。
所以總結,spring提供了兩種HandlerMapping以及三種HandlerAdapter.他們運行匹配的關係如圖:
那麼運行時他怎麼能找到這些呢?spring是怎麼配置提供的呢?
其實他們在spring配置文件就已經配置好了,當springMVC初始化時就加載實例化,獲取這些對象。他們是被配置在spring的SpringwebMVC架包的servlet架包中的DispatcherServlet.properties配置文件中
DispatcherServlet源碼流程分析
1.執行doDispatch2.調用getHandler方法獲取請求目標的方法 也就是 請求url映射路徑對應的控制層具體的方法
handlerMappings的做用查找控制器位置,好比xml和註解方式。
3.調用getHandlerAdapter獲取控制層適配器 RequestMappingHandlerAdapter
4.執行攔截器前置方法 preHandle() 若是返回爲true的話
5.執行實際請求目標方法 返回modeAndView對象
6.執行攔截器PostHandle()方法
7.設置渲染視圖層內容
8.執行攔截器afterCompletion方法
DispatcherServlet#initHandlerMappings
/** * Initialize the HandlerMappings used by this class. * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, * we default to BeanNameUrlHandlerMapping. */ private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; if (this.detectAllHandlerMappings) { // Find all HandlerMappings in the ApplicationContext, including ancestor contexts. Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { // Ignore, we'll add a default HandlerMapping later. } } // Ensure we have at least one HandlerMapping, by registering // a default HandlerMapping if no other mappings are found. if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
servlet初始化會調用 init 方法,換句話說就是springMVC進行初始化的時候首先會去執行HttpServletBean的init方法, 下面看看HttpServletBean的源碼:
/** * Map config parameters onto bean properties of this servlet, and * invoke subclass initialization. * @throws ServletException if bean properties are invalid (or required * properties are missing), or if subclass initialization fails. */ @Override public final void init() throws ServletException { // Set bean properties from init parameters. 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) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. initServletBean(); }
獲取你在web.xml中配置在<init-param>中的屬性(例如: namespace, contextConfigLocation)。 其中有一點值得注意,那就是 initServletBean() 這個方法是由其子類 FrameworkServlet 實現,所以, 接下來 FramworkServlet 會執行 initServletBean 這個方法,下面就繼續看看 initServletBean 方法源碼:
FrameWorkservlet#initServletBean
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } long startTime = System.currentTimeMillis(); try { this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } }
initServletBean 方法中就調用了一個 initFrameworkServlet 方法和 initWebApplicationContext 方法,其中initFrameworkServlet方法是由子類實現,這個很少說,直接看 initWebApplicationContext 方法源碼:
protected WebApplicationContext initWebApplicationContext() { //此處的 rootContext 在你配置了ContextLoaderListener的時候注入的 //經過分析ContextLoaderListenr的源碼,能夠看到 //ContextLoaderListener經過ContextLoader根據ApplicationContext.xml的配置會建立一個xmlWebApplicationContext //若是沒有配置ContextLoaderListener,本處將爲null,但不影響springMVC,爲什麼?經過接下來的分析,就能看到緣由 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext()); WebApplicationContext wac = null; //當webApplicationContext已經存在,那麼就直接使用,使用以前會先設置rootContext,爲其跟。 //配置完成以後refresh一次,refresh會涉及到IOC的內容,本處不作探討。 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); } this.configureAndRefreshWebApplicationContext(cwac); } } } //若是不存在webApplicationContext,那麼先去ServletContext中查找 if (wac == null) { wac = this.findWebApplicationContext(); } //若是上述沒有查到,那麼就建立webApplicationContext if (wac == null) { wac = this.createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { //此方法由DispatcherServlet調用 this.onRefresh(wac); } //將webApplicationContext保存在ServletContext if (this.publishContext) { //將上下文發佈爲servlet上下文屬性。 String attrName = this.getServletContextAttributeName(); this.getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { //此處的contextClass 可在web.xml 中的《init-param》中指定 //若是沒有配置,那麼默認的是XmlWebApplicationContext.class Class<?> contextClass = this.getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + this.getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "', using parent context [" + parent + "]"); } if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } else { //此處利用反射建立 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setEnvironment(this.getEnvironment()); wac.setParent(parent); String configLocation = this.getContextConfigLocation(); if (configLocation != null) { wac.setConfigLocation(configLocation); } //refresh一次,這裏很少說 this.configureAndRefreshWebApplicationContext(wac); return wac; }
SpringMVC控制層容器初始化
- HttpServletBean init ()方法
- FrameworkServlet initServletBean方法→ initWebApplicationContext();
- DispatcherServlet onRefresh方法→ initStrategies()方法