
目录一、DispatcherServlet1.RequestMapping作用1.1 原始JavaWeb的缺陷1.2 RequestMapping2.DispatcherServlet配置2.1 基于web.xml2.2 基于AbstractDispatcherServletInitializer类3. DispatcherServlet源码1.1 继承关系1.2 doDispatch()方法二、SpringMVC执行流程一、DispatcherServlet1.RequestMapping作用1.1 原始JavaWeb的缺陷在原始的JavaWeb中我们通过在web.xml中配置servlet类的类路径-URL映射关系从而实现Servlet和URL的一一对应关系!--servlet描述信息--servletservlet-name/servlet-name!--这个位置是带有包名的全限定类名--servlet-class/servlet-class/servlet!--servlet映射信息--servlet-mappingservlet-name/servlet-name!--这里给Servlet类规定一个URL路径路径名必须以 / 开始--url-pattern/url-pattern/servlet-mapping!--原理服务器通过URL中的url-pattern寻找这个路径的servlet-name通过servlet-name匹配对应的class文件--!--实际上web服务器启动时将所有servlet以键值对的方式url-patternservlet-class添加到一个map集合中--此后为了简化xml的配置我们使用了WebServlet注解WebServlet(namehello,urlPatterns/dept/ServletA,loadOnStartup1,initParams{WebInitParam(name10,valueasd)})classServletAextendsHttpServlet{OverrideprotectedvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{}OverrideprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{}}1.2 RequestMapping然而原始的JavaWeb实现servlet仍然较为复杂因为一个servlet类仅能对应的一个URL容易造成类爆炸。所以SpringMVC使用了DispatcherServletDispatcherServlet继承自HttpServlet是SpringMVC中唯一的Servlet负责拦截所有请求根据维护的HandlerMapping记录URL-具体方法Handler 之间的映射关系找到IoC容器中处理该请求的RequestMapping(URL)方法并执行。被RequestMapping()修饰的类都是普通的JavaBean不是Servlet。Controller//实例化Bean到IoC容器RequestMapping(/a)//访问路径publicclassReqAction{//接收get请求RequestMapping(value/req.action,methodRequestMethod.GET)publicStringreq1(){System.out.println(我处理get请求);returnmain;}//接收post请求RequestMapping(value/req.action,methodRequestMethod.POST)publicStringreq2(){System.out.println(我处理post请求);returnmain;}}2.DispatcherServlet配置DispatcherServlet继承自HttpServlet是SpringMVC中唯一的Servlet所以配置方式和JavaWeb中servlet的配置方式相同。不同点是原始的Servlet只负责处理url-pattern的单个请求而DispatcherServlet需要处理所有请求所以url-pattern要配置为/表示该serlvet处理所有请求。被RequestMapping()修饰的类都是普通的JavaBean不是Servlet。由于DispatcherServlet需要知道都有哪些Bean使用了RequestMapping()才能在接受请求时使用对应的方法处理请求所以需要获取所有配置了RequestMapping()的类这时候应该能想到IoC容器了直接实例化一个DispatcherServlet专用的IoC容器通过包扫描的方式将配置了RequestMapping()的类也就是扫描controller包中标注了Component的类加入到IoC容器然后将IoC容器提交给DispatcherServlet通过遍历每个单例的方法维护DispatcherServlet的MapURL,Method集合。需要说明只扫描controller包下的Bean就可以获取所有RequestMapping()的前提是你得在controller包下的Component()类用RequestMapping()如果你在其他位置用RequestMapping()那就要同时扫描对应的包注入到IoC容器这里只扫描controller包下的Component()仅仅是基于良好的项目规范因为正常来讲就是在controller包下的Component()类用RequestMapping()所以巧妙地复用了这个规范从而能扫描到所有RequestMapping()的类到IoC容器。点击跳转Spring IoC笔记2.1 基于web.xml首先扫描controller包下的Bean注入到IoC容器然后采用init-param标签的形式将IoC容器传给DispatcherServlet。!--servlet描述信息--servletservlet-nameDispatcherServlet/servlet-name!--这个位置是带有包名的全限定类名这里的DispatcherServlet不是自己写的类是Springmvc包中的类--servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-classload-on-startup1/load-on-startup!--配置servlet的局部参数DispatcherServlet要求传入IoC容器中用于处理响应的Bean对象--init-paramparam-namecontextConfigLocation/param-name!--springmvc.xml中仅配置了包扫描扫描处理请求的类方法配置了RequestMapping(URL)的类扫描controller包下就可以了--param-valueclasspath:springmvc.xml/param-value/init-param/servlet!--servlet映射信息--servlet-mappingservlet-nameDispatcherServlet/servlet-name!--这里给Servlet类规定一个URL路径路径名必须以/开始在JavaWeb中我们是对每个servlet配置具体的URL而在SpringMVC中由于只有一个Servlet所以我们使用该Servlet来匹配所有URL--url-pattern//url-pattern/servlet-mapping?xml version1.0 encodingUTF-8?beansxmlnshttp://www.springframework.org/schema/beansxmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd!--配置扫描的包路径扫描注解从而创建对象到IoC容器中--context:component-scanbase-packageorg.example.controller//bean2.2 基于AbstractDispatcherServletInitializer类对于springmvc.xml文件中的内容在SpringIoC容器中已经学习过了直接使用配置类封装// 封装了springmvc.xml文件Configuration// 封装了xml文件中的文件头ComponentScan(org.example.controller)// 封装了了包扫描publicclassRequestMappingComponentScan{}对于web.xml文件中对于DispatcherServlet的配置SpringMVC提供了AbstractDispatcherServletInitializer类通过继承该类实现三个抽象方法完成serlvet处理请求URL路径的定义、将IoC容器传给DispatcherServlet。publicclassMyDispatcherServletextendsAbstractDispatcherServletInitializer{// 配置Servlet处理哪些URLOverrideprotectedString[]getServletMappings(){returnnewString[]{/};}// 将IoC容器传给DispatcherServlet这样DispatcherServlet就知道有哪些RequestMapping()方法了OverrideprotectedWebApplicationContextcreateServletApplicationContext(){// 实例化一个新的IoC容器AnnotationConfigWebApplicationContextcontextnewAnnotationConfigWebApplicationContext();// 将RequestMapping注解的类添加到IoC容器中context.register(RequestMappingComponentScan.class);// 返回IoC容器returncontext;}OverrideprotectedWebApplicationContextcreateRootApplicationContext(){returnnull;}}3. DispatcherServlet源码1.1 继承关系可以看到DispatcherServlet本质是实现了Servlet接口、继承了HttpServletTomcat本质还是调用Servlet的service()方法完成请求的处理而HttpServlet的service()方法会根据请求类型调用对应的doGet()、doPost()方法所以FrameworkServlet重写了doGet()、doPost()方法全都调用processRequest()方法processRequest()方法也是基于模板方法模式调用抽象方法doService()所以就要求子类必须实现doService()方法所以DispatcherServlet继承了FrameworkServlet重写了doService()方法调用doDispatch()方法。简单来说Tomcat会调用DispatcherServlet的doDispatch()方法来处理请求。publicclassDispatcherServletextendsFrameworkServlet{protectedvoiddoService(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{try{this.doDispatch(request,response);}}}publicabstractclassFrameworkServletextendsHttpServletimplementsApplicationContextAware{protectedfinalvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{this.processRequest(request,response);}protectedfinalvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{this.processRequest(request,response);}protectedfinalvoiddoPut(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{this.processRequest(request,response);}protectedfinalvoidprocessRequest(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{try{this.doService(request,response);}}}publicabstractclassHttpServletextendsGenericServlet{// Tomcat调用该方法进行请求的处理publicvoidservice(ServletRequestreq,ServletResponseres)throwsServletException,IOException{if(reqinstanceofHttpServletRequestresinstanceofHttpServletResponse){HttpServletRequestrequest(HttpServletRequest)req;HttpServletResponseresponse(HttpServletResponse)res;// 模板方法模式调用HttpServlet自定义的service()方法this.service(request,response);}else{thrownewServletException(non-HTTP request or response);}}// 根据请求类型执行对应类型的方法子类继承HttpServlet需要重写doGet()、doPost()等方法protectedvoidservice(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{Stringmethodreq.getMethod();if(method.equals(GET)){lastModifiedthis.getLastModified(req);if(lastModified-1L){this.doGet(req,resp);}else{longifModifiedSincereq.getDateHeader(If-Modified-Since);if(ifModifiedSincelastModified){this.maybeSetLastModified(resp,lastModified);this.doGet(req,resp);}else{resp.setStatus(304);}}}elseif(method.equals(HEAD)){lastModifiedthis.getLastModified(req);this.maybeSetLastModified(resp,lastModified);this.doHead(req,resp);}elseif(method.equals(POST)){this.doPost(req,resp);}elseif(method.equals(PUT)){this.doPut(req,resp);}elseif(method.equals(DELETE)){this.doDelete(req,resp);}elseif(method.equals(OPTIONS)){this.doOptions(req,resp);}elseif(method.equals(TRACE)){this.doTrace(req,resp);}else{StringerrMsglStrings.getString(http.method_not_implemented);Object[]errArgsnewObject[]{method};errMsgMessageFormat.format(errMsg,errArgs);resp.sendError(501,errMsg);}}protectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{Stringprotocolreq.getProtocol();StringmsglStrings.getString(http.method_get_not_supported);resp.sendError(this.getMethodNotSupportedCode(protocol),msg);}protectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{Stringprotocolreq.getProtocol();StringmsglStrings.getString(http.method_post_not_supported);resp.sendError(this.getMethodNotSupportedCode(protocol),msg);}}publicabstractclassGenericServletimplementsServlet,ServletConfig,Serializable{}publicinterfaceServlet{}1.2 doDispatch()方法doDispatch()方法中通过getHandler()获取拦截器、请求URL对应的HandlerRequestMapping方法然后调用handle()方法执行拦截器和Handler。protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{HttpServletRequestprocessedRequestrequest;HandlerExecutionChainmappedHandlernull;booleanmultipartRequestParsedfalse;WebAsyncManagerasyncManagerWebAsyncUtils.getAsyncManager(request);try{try{ModelAndViewmvnull;ObjectdispatchExceptionnull;try{processedRequestthis.checkMultipart(request);multipartRequestParsedprocessedRequest!request;mappedHandlerthis.getHandler(processedRequest);if(mappedHandlernull){this.noHandlerFound(processedRequest,response);return;}HandlerAdapterhathis.getHandlerAdapter(mappedHandler.getHandler());Stringmethodrequest.getMethod();booleanisGetHttpMethod.GET.matches(method);if(isGet||HttpMethod.HEAD.matches(method)){longlastModifiedha.getLastModified(request,mappedHandler.getHandler());if((newServletWebRequest(request,response)).checkNotModified(lastModified)isGet){return;}}if(!mappedHandler.applyPreHandle(processedRequest,response)){return;}mvha.handle(processedRequest,response,mappedHandler.getHandler());if(asyncManager.isConcurrentHandlingStarted()){return;}this.applyDefaultViewName(processedRequest,mv);mappedHandler.applyPostHandle(processedRequest,response,mv);}catch(Exceptionvar20){dispatchExceptionvar20;}catch(Throwablevar21){dispatchExceptionnewServletException(Handler dispatch failed: String.valueOf(var21),var21);}this.processDispatchResult(processedRequest,response,mappedHandler,mv,(Exception)dispatchException);}catch(Exceptionvar22){triggerAfterCompletion(processedRequest,response,mappedHandler,var22);}catch(Throwablevar23){triggerAfterCompletion(processedRequest,response,mappedHandler,newServletException(Handler processing failed: String.valueOf(var23),var23));}}finally{if(asyncManager.isConcurrentHandlingStarted()){if(mappedHandler!null){mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest,response);}asyncManager.setMultipartRequestParsed(multipartRequestParsed);}elseif(multipartRequestParsed||asyncManager.isMultipartRequestParsed()){this.cleanupMultipart(processedRequest);}}}二、SpringMVC执行流程客户端向服务器发送HTTP请求执行Filter的过滤操作根据请求的URL调用对应Servlet由于SpringMVC只有一个Servlet且配置的响应路径是all所以调用DispatcherServlet。这一步是Tomcat执行的下面才是SpringMVC执行流程符合拦截规则的URI被DispatcherServlet控制器捕获。DispatcherServlet对请求的URL进行解析调用HandlerMapping匹配到Handler标注RequestMapping的方法。执行拦截器HandlerInterceptor的preHandle()方法。将请求参数转换为Handler的形参。调用Handler返回结果。执行拦截器HandlerInterceptor的postHandle()方法。如果使用了ResponseBody调用Jackon将返回值序列化为JSON字符串。将JSON字符串写入HttpServletResponse的响应体Body中。执行拦截器HandlerInterceptor的afterCompletion()方法。将响应状态码、响应头、JSON 数据返回给前端。