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()方法