SpringMVC执行过程解析
做过java开发的人都知道spring,就算目前不知道,慢慢也会知道,由于spring体系庞大,模块众多,我就介绍下业务开发(以多年经历的认识,90%的人都是做业务开发的)时用到的springmvc。
首先,让我们从 spring mvc 的四大组件:前端控制器(dispatcherservlet)、处理器映射器(handlermapping)、处理器适配器(handleradapter)以及视图解析器(viewresolver) 的角度来看一下 spring mvc 对用户请求的处理过程,
springmvc 执行过程
- 用户请求发送到前端控制器 dispatcherservlet。
- 前端控制器 dispatcherservlet 接收到请求后,dispatcherservlet 会使用 handlermapping 来处理,handlermapping 会查找到具体进行处理请求的 handler 对象。
- handlermapping 找到对应的 handler 之后,并不是返回一个 handler 原始对象,而是一个 handler 执行链(handlerexecutionchain),在这个执行链中包括了拦截器和处理请求的 handler。handlermapping 返回一个执行链给 dispatcherservlet。
- dispatcherservlet 接收到执行链之后,会调用 handler 适配器去执行 handler。
- handleradapter执行完成 handler之后会得到一个 modelandview,并返回给 dispatcherservlet。
- dispatcherservlet 接收到 handleradapter 返回的 modelandview 之后,会根据其中的视图名调用 viewresolver。
- viewresolver 根据逻辑视图名解析成一个真正的 view 视图,并返回给 dispatcherservlet。
- dispatcherservlet 接收到视图之后,会根据上面的 modelandview 中的 model 来进行视图中数据的填充,也就是所谓的视图渲染。
- 渲染完成之后,dispatcherservlet 就可以将结果返回给用户了。
在了解了大概的执行过程后,让我们一起从源码角度去深入探索。
源码解析
首先当我们访问url的时候,将会把请求发送到前端控制器 dispatcherservlet,dispatcherservlet 是一个 servlet,我们知道在 servlet 在处理一个请求的时候会交给 service 方法进行处理,这里也不例外,dispatcherservlet 继承了 frameworkservlet,首先进入 frameworkservlet 的 service 方法:
1 protected void service(httpservletrequest request, httpservletresponse response) throws servletexception, ioexception { 2 // 请求方法 3 httpmethod httpmethod =httpmethod.resolve(request.getmethod()); 4 // 若方法为 patch 方法或为空则单独处理 5 if (httpmethod == httpmethod.patch || httpmethod == null) { 6 processrequest(request, response); 7 } else { 8 // 其他的请求类型的方法经由父类,也就是 httpservlet 处理 9 super.service(request, response); 10 } 11 }
httpservlet 中会根据请求类型的不同分别调用 doget 或者 dopost 等方法,frameworkservlet 中已经重写了这些方法,在这些方法中会调用 processrequest 进行处理,在 processrequest 中会调用 doservice 方法,这个 doservice 方法就是在 dispatcherservlet 中实现的。下面就看下 dispatcherservlet 中的 doservice 方法的实现。
dispatcherservlet 收到请求
dispatcherservlet 中的 doservice方法:
1 protected void doservice(httpservletrequest request, httpservletresponse response) throws exception { 2 logrequest(request); 3 // 给 request 中的属性做一份快照,以便能够恢复原始属性 4 map<string, object> attributessnapshot = null; 5 if (webutils.isincluderequest(request)) { 6 attributessnapshot = new hashmap<>(); 7 enumeration<?> attrnames = request.getattributenames(); 8 while (attrnames.hasmoreelements()) { 9 string attrname = (string) attrnames.nextelement(); 10 if (this.cleanupafterinclude || attrname.startswith(default_strategies_prefix)) { 11 attributessnapshot.put(attrname, request.getattribute(attrname)); 12 } 13 } 14 } 15 // 如果没有配置本地化或者主题的处理器之类的,springmvc 会使用默认的配置文件,即 dispatcherservlet.properties 16 request.setattribute(web_application_context_attribute, getwebapplicationcontext()); 17 request.setattribute(locale_resolver_attribute, this.localeresolver); 18 request.setattribute(theme_resolver_attribute, this.themeresolver); 19 request.setattribute(theme_source_attribute, getthemesource()); 20 if (this.flashmapmanager != null) { 21 flashmap inputflashmap = this.flashmapmanager.retrieveandupdate(request, response); 22 if (inputflashmap != null) { 23 request.setattribute(input_flash_map_attribute, collections.unmodifiablemap(inputflashmap)); 24 } 25 request.setattribute(output_flash_map_attribute, new flashmap()); 26 request.setattribute(flash_map_manager_attribute, this.flashmapmanager); 27 } 28 29 try { 30 // 开始真正的处理 31 dodispatch(request, response); 32 } 33 finally { 34 if (!webasyncutils.getasyncmanager(request).isconcurrenthandlingstarted()) { 35 // 恢复原始属性快照 36 if (attributessnapshot != null) { 37 restoreattributesafterinclude(request, attributessnapshot); 38 } 39 } 40 } 41 }
接下来 dispatcherservlet 开始真正的处理,让我们来看下 dodispatch 方法,首先会获取当前请求的 handler 执行链,然后找到合适的 handleradapter,接着调用 requestmappinghandleradapter 的 handle 方法,如下为 dodispatch 方法:
1 protected void dodispatch(httpservletrequest request, httpservletresponse response) throws exception { 2 httpservletrequest processedrequest = request; 3 handlerexecutionchain mappedhandler = null; 4 boolean multipartrequestparsed = false; 5 webasyncmanager asyncmanager = webasyncutils.getasyncmanager(request); 6 try { 7 modelandview mv = null; 8 exception dispatchexception = null; 9 try { 10 // 先检查是不是 multipart 类型的,比如上传等;如果是 multipart 类型的,则转换为 multiparthttpservletrequest 类型 11 processedrequest = checkmultipart(request); 12 multipartrequestparsed = (processedrequest != request); 13 14 // 获取当前请求的 handler 执行链 15 mappedhandler = gethandler(processedrequest); 16 if (mappedhandler == null) { 17 nohandlerfound(processedrequest, response); 18 return; 19 } 20 21 // 获取当前请求的 handler 适配器 22 handleradapter ha = gethandleradapter(mappedhandler.gethandler()); 23 24 // 对于 header 中 last-modified 的处理 25 string method = request.getmethod(); 26 boolean isget = "get".equals(method); 27 if (isget || "head".equals(method)) { 28 long lastmodified = ha.getlastmodified(request, mappedhandler.gethandler()); 29 if (new servletwebrequest(request, response).checknotmodified(lastmodified) && isget) { 30 return; 31 } 32 } 33 34 // 遍历所有定义的 interceptor,执行 prehandle 方法 35 if (!mappedhandler.applyprehandle(processedrequest, response)) { 36 return; 37 } 38 39 // 实际调用 handler 的地方 40 mv = ha.handle(processedrequest, response, mappedhandler.gethandler()); 41 42 if (asyncmanager.isconcurrenthandlingstarted()) { 43 return; 44 } 45 // 处理成默认视图名,也就是添加前缀和后缀等 46 applydefaultviewname(processedrequest, mv); 47 // 拦截器posthandle方法进行处理 48 mappedhandler.applyposthandle(processedrequest, response, mv); 49 } 50 catch (exception ex) { 51 dispatchexception = ex; 52 } 53 catch (throwable err) { 54 dispatchexception = new nestedservletexception("handler dispatch failed", err); 55 } 56 // 处理最后的结果,渲染之类的都在这里 57 processdispatchresult(processedrequest, response, mappedhandler, mv, dispatchexception); 58 } 59 catch (exception ex) { 60 triggeraftercompletion(processedrequest, response, mappedhandler, ex); 61 } 62 catch (throwable err) { 63 triggeraftercompletion(processedrequest, response, mappedhandler, 64 new nestedservletexception("handler processing failed", err)); 65 } 66 finally { 67 if (asyncmanager.isconcurrenthandlingstarted()) { 68 if (mappedhandler != null) { 69 mappedhandler.applyafterconcurrenthandlingstarted(processedrequest, response); 70 } 71 } 72 else { 73 if (multipartrequestparsed) { 74 cleanupmultipart(processedrequest); 75 } 76 } 77 } 78 }
查找对应的 handler 对象
查找对应的 handler 对象
让我们去探索下是如何获取当前请求的 handler 执行链,对应着这句代码 mappedhandler = gethandler(processedrequest);
,看下 dispatcherservlet 具体的 gethandler 方法,该方法主要是遍历所有的 handlermappings 进行处理,handlermappings 是在启动的时候预先注册好的,在循环中会调用 abstracthandlermapping 类中的 gethandler 方法来获取 handler 执行链,若获取的 handler 执行链不为 null,则返回当前请求的 handler 执行链,dispatcherservlet 类的 gethandler 方法如下:
1 protected handlerexecutionchain gethandler(httpservletrequest request) throws exception { 2 if (this.handlermappings != null) { 3 // 遍历所有的 handlermappings 进行处理,handlermappings 是在启动的时候预先注册好的 4 for (handlermapping mapping : this.handlermappings) { 5 handlerexecutionchain handler = mapping.gethandler(request); 6 if (handler != null) { 7 return handler; 8 } 9 } 10 } 11 return null; 12 }
在循环中,根据 mapping.gethandler(request);,继续往下看 abstracthandlermapping 类中的 gethandler 方法:
1 public final handlerexecutionchain gethandler(httpservletrequest request) throws exception { 2 // 根据 request 获取 handler 3 object handler = gethandlerinternal(request); 4 if (handler == null) { 5 // 如果没有找到就使用默认的 handler 6 handler = getdefaulthandler(); 7 } 8 if (handler == null) { 9 return null; 10 } 11 // 如果 handler 是 string,表明是一个 bean 名称,需要寻找对应 bean 12 if (handler instanceof string) { 13 string handlername = (string) handler; 14 handler = obtainapplicationcontext().getbean(handlername); 15 } 16 // 封装 handler 执行链 17 return gethandlerexecutionchain(handler, request); 18 }
abstracthandlermapping 类中的 gethandler 方法中首先根据 requrst 获取 handler,主要是调用了 abstracthandlermethodmapping 类中的 gethandlerinternal 方法,该方法首先获取 request 中的 url,即 /testspringmvc,用来匹配 handler 并封装成 handlermethod,然后根据 handlermethod 中的 bean 来实例化 handler 并返回。
1 protected handlermethod gethandlerinternal(httpservletrequest request) throws exception { 2 // 获取 request 中的 url,用来匹配 handler 3 string lookuppath = geturlpathhelper().getlookuppathforrequest(request); 4 request.setattribute(lookup_path, lookuppath); 5 this.mappingregistry.acquirereadlock(); 6 try { 7 // 根据路径寻找 handler,并封装成 handlermethod 8 handlermethod handlermethod = lookuphandlermethod(lookuppath, request); 9 // 根据 handlermethod 中的 bean 来实例化 handler,并添加进 handlermethod 10 return (handlermethod != null ? handlermethod.createwithresolvedbean() : null); 11 } 12 finally { 13 this.mappingregistry.releasereadlock(); 14 } 15 }
接下来,我们看 lookuphandlermethod 的逻辑,主要逻辑委托给了 mappingregistry 这个成员变量来处理:
1 protected handlermethod lookuphandlermethod(string lookuppath, httpservletrequest request) throws exception { 2 list<match> matches = new arraylist<>(); 3 // 通过 lookuppath 属性中查找。如果找到了,就返回对应的requestmappinginfo 4 list<t> directpathmatches = this.mappingregistry.getmappingsbyurl(lookuppath); 5 if (directpathmatches != null) { 6 // 如果匹配到了,检查其他属性是否符合要求,如请求方法,参数,header 等 7 addmatchingmappings(directpathmatches, matches, request); 8 } 9 if (matches.isempty()) { 10 // 没有直接匹配到,则遍历所有的处理方法进行通配符匹配 11 addmatchingmappings(this.mappingregistry.getmappings().keyset(), matches, request); 12 } 13 14 if (!matches.isempty()) { 15 // 如果方法有多个匹配,不同的通配符等,则排序选择出最合适的一个 16 comparator<match> comparator = new matchcomparator(getmappingcomparator(request)); 17 matches.sort(comparator); 18 match bestmatch = matches.get(0); 19 // 如果有多个匹配的,会找到第二个最合适的进行比较 20 if (matches.size() > 1) { 21 if (logger.istraceenabled()) { 22 logger.trace(matches.size() + " matching mappings: " + matches); 23 } 24 if (corsutils.ispreflightrequest(request)) { 25 return preflight_ambiguous_match; 26 } 27 match secondbestmatch = matches.get(1); 28 if (comparator.compare(bestmatch, secondbestmatch) == 0) { 29 method m1 = bestmatch.handlermethod.getmethod(); 30 method m2 = secondbestmatch.handlermethod.getmethod(); 31 string uri = request.getrequesturi(); 32 // 不能有相同的最优 match 33 throw new illegalstateexception( 34 "ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}"); 35 } 36 } 37 request.setattribute(best_matching_handler_attribute, bestmatch.handlermethod); 38 // 设置 request 参数(requestmappinghandlermapping 对其进行了覆写) 39 handlematch(bestmatch.mapping, lookuppath, request); 40 // 返回匹配的 url 的处理的方法 41 return bestmatch.handlermethod; 42 } 43 else { 44 // 调用 requestmappinghandlermapping 类的 handlenomatch 方法再匹配一次 45 return handlenomatch(this.mappingregistry.getmappings().keyset(), lookuppath, request); 46 } 47 }
通过上面的过程,我们就获取到了 handler,就开始封装执行链了,就是将我们配置的拦截器加入到执行链中去,gethandlerexecutionchain 方法如下:
1 protected handlerexecutionchain gethandlerexecutionchain(object handler, httpservletrequest request) { 2 // 如果当前 handler 不是执行链类型,就使用一个新的执行链实例封装起来 3 handlerexecutionchain chain = (handler instanceof handlerexecutionchain ? (handlerexecutionchain) handler : new handlerexecutionchain(handler)); 4 5 string lookuppath = this.urlpathhelper.getlookuppathforrequest(request, lookup_path); 6 // 遍历拦截器,找到跟当前 url 对应的,添加进执行链中去 7 for (handlerinterceptor interceptor : this.adaptedinterceptors) { 8 if (interceptor instanceof mappedinterceptor) { 9 mappedinterceptor mappedinterceptor = (mappedinterceptor) interceptor; 10 if (mappedinterceptor.matches(lookuppath, this.pathmatcher)) { 11 chain.addinterceptor(mappedinterceptor.getinterceptor()); 12 } 13 } 14 else { 15 chain.addinterceptor(interceptor); 16 } 17 } 18 return chain; 19 }
到此为止,我们就获取了当前请求的 handler 执行链,接下来看下是如何获取请求的 handler 适配器,主要依靠 dispatcherservlet 类的 gethandleradapter 方法,该方法就是遍历所有的 handleradapter,找到和当前 handler 匹配的就返回,在这里匹配到的为 requestmappinghandleradapter。
dispatcherservlet 类的 gethandleradapter 方法如下:
1 protected handleradapter gethandleradapter(object handler) throws servletexception { 2 if (this.handleradapters != null) { 3 // 遍历所有的 handleradapter,找到和当前 handler 匹配的就返回 4 for (handleradapter adapter : this.handleradapters) { 5 if (adapter.supports(handler)) { 6 return adapter; 7 } 8 } 9 } 10 throw new servletexception("no adapter for handler [" + handler + 11 "]: the dispatcherservlet configuration needs to include a handleradapter that supports this handler"); 12 }
handleradapter 执行当前的 handler
再获取完当前请求的 handler 适配器后,接着进行缓存处理,也就是对 last-modified 的处理,然后调用 applyprehandle 方法执行拦截器的 prehandle 方法,即遍历所有定义的 interceptor,执行 prehandle 方法,然后就到了实际执行 handle 的地方,dodispatch 方法中 handle 方法是执行当前 handler,我们这里使用的是 requestmappinghandleradapter,首先会进入 abstracthandlermethodadapter 的 handle 方法:
1 public final modelandview handle(httpservletrequest request, httpservletresponse response, object handler) throws exception { 2 return handleinternal(request, response, (handlermethod) handler); 3 }
在 abstracthandlermethodadapter 的 handle 方法中又调用了 requestmappinghandleradapter 类的 handleinternal 方法:
1 protected modelandview handleinternal(httpservletrequest request, httpservletresponse response, handlermethod handlermethod) throws exception { 2 modelandview mav; 3 checkrequest(request); 4 if (this.synchronizeonsession) { 5 httpsession session = request.getsession(false); 6 if (session != null) { 7 object mutex = webutils.getsessionmutex(session); 8 synchronized (mutex) { 9 mav = invokehandlermethod(request, response, handlermethod); 10 } 11 } 12 else { 13 mav = invokehandlermethod(request, response, handlermethod); 14 } 15 } 16 else { 17 // 执行方法,封装 modelandview 18 mav = invokehandlermethod(request, response, handlermethod); 19 } 20 if (!response.containsheader(header_cache_control)) { 21 if (getsessionattributeshandler(handlermethod).hassessionattributes()) { 22 applycacheseconds(response, this.cachesecondsforsessionattributehandlers); 23 } 24 else { 25 prepareresponse(response); 26 } 27 } 28 return mav; 29 }
在执行完 handle 方法后,然后调用 applydefaultviewname 方法组装默认视图名称,将前缀和后缀名都加上,接着调用 applyposthandle 方法执行拦截器的 prehandle 方法,也就是遍历所有定义的 interceptor,执行prehandle 方法。
处理最终结果以及渲染
最终调用 dispatcherservlet 类中的 processdispatchresult 方法,此方法主要是处理最终结果的,包括异常处理、渲染页面和发出完成通知触发拦截器的 aftercompletion() 方法执行等。processdispatchresult()方法代码如下:
1 private void processdispatchresult(httpservletrequest request, httpservletresponse response, handlerexecutionchain mappedhandler, modelandview mv, exception exception) throws exception { 2 boolean errorview = false; 3 if (exception != null) { 4 if (exception instanceof modelandviewdefiningexception) { 5 logger.debug("modelandviewdefiningexception encountered", exception); 6 mv = ((modelandviewdefiningexception) exception).getmodelandview(); 7 } 8 else { 9 object handler = (mappedhandler != null ? mappedhandler.gethandler() : null); 10 mv = processhandlerexception(request, response, handler, exception); 11 errorview = (mv != null); 12 } 13 } 14 if (mv != null && !mv.wascleared()) { 15 // 渲染 16 render(mv, request, response); 17 if (errorview) { 18 webutils.clearerrorrequestattributes(request); 19 } 20 } 21 else { 22 if (logger.istraceenabled()) { 23 logger.trace("no view rendering, null modelandview returned."); 24 } 25 } 26 if (webasyncutils.getasyncmanager(request).isconcurrenthandlingstarted()) { 27 return; 28 } 29 if (mappedhandler != null) { 30 mappedhandler.triggeraftercompletion(request, response, null); 31 } 32 }
接下来让我们看下 dispatcherservlet 类的 render 方法是如何完成渲染的,dispatcherservlet 类的 render 方法渲染过程如下:
- 判断 modelandview 中 view 是否为 view name,没有获取其实例对象:如果是根据 name,如果是则需要调用 resolveviewname 从视图解析器获取对应的视图(view)对象;否则 modelandview 中使用 getview 方法获取 view 对象。
- 然后调用 view 类的 render 方法。
dispatcherservlet 类的 render 方法如下:
1 protected void render(modelandview mv, httpservletrequest request, httpservletresponse response) throws exception { 2 // 设置本地化 3 locale locale = (this.localeresolver != null ? this.localeresolver.resolvelocale(request) : request.getlocale()); 4 response.setlocale(locale); 5 view view; 6 string viewname = mv.getviewname(); 7 if (viewname != null) { 8 // 解析视图名,得到视图 9 view = resolveviewname(viewname, mv.getmodelinternal(), locale, request); 10 if (view == null) { 11 throw new servletexception("could not resolve view with name '" + mv.getviewname() + 12 "' in servlet with name '" + getservletname() + "'"); 13 } 14 } 15 else { 16 view = mv.getview(); 17 if (view == null) { 18 throw new servletexception("modelandview [" + mv + "] neither contains a view name nor a " + 19 "view object in servlet with name '" + getservletname() + "'"); 20 } 21 } 22 23 if (logger.istraceenabled()) { 24 logger.trace("rendering view [" + view + "] "); 25 } 26 try { 27 if (mv.getstatus() != null) { 28 response.setstatus(mv.getstatus().value()); 29 } 30 // 委托给视图进行渲染 31 view.render(mv.getmodelinternal(), request, response); 32 } 33 catch (exception ex) { 34 if (logger.isdebugenabled()) { 35 logger.debug("error rendering view [" + view + "]", ex); 36 } 37 throw ex; 38 } 39 }
假设我们用的是 thymeleaf 模版引擎(也有其他模板引擎比如freemarker等),所以 view.render 找到对应的视图 thymeleafview 的 render 方法进行渲染。
1 public void render(final map<string, ?> model, final httpservletrequest request, final httpservletresponse response) throws exception { 2 renderfragment(this.markupselectors, model, request, response); 3 }
thymeleafview 的 render 方法又调用 renderfragment 方法进行视图渲染,渲染完成之后,dispatcherservlet 就可以将结果返回给我们了。
总结
springmvc的执行流程简单介绍完了(不足之处多多指教),web框架的处理流程可以说一样的思路(本人以前用django,python的web框架)做过开发,如果不是看编程语言的话,会以为说的是django流程呢!!!,都是为了快速开发快速迭代业务系统,不像早期需要编写原生的servlet,比如
上一篇: 大一0基础小白用最基础C写哥德巴赫猜想
推荐阅读
-
解析如何通过PHP函数获取当前运行的环境 来进行判断执行逻辑(小技巧)_php技巧
-
javascript new fun的执行过程_js面向对象
-
小觑数据库(SqlServer)查询语句执行过程
-
在Oracle中不通过存储过程一次执行多条SQL语句Oracle PL/SQL
-
解析:php调用MsSQL存储过程使用内置RETVAL获取过程中的return值_PHP
-
MySQL查询语句执行过程及性能优化-查询过程及优化方法(JOIN/ORD_MySQL
-
Sql的执行过程说明
-
SQL语句执行过程原理讲解
-
MySQL 存储过程中执行动态SQL语句的方法_MySQL
-
Android自定义View过程解析