本篇文章来了解下SpringMVC的执行过程及源码分析。
SpringMVC目前是市面上主流的web控制层框架,后端的接口都是由他来接收请求后才由自己的代码进行处理,那么他是怎么接收到前端页面发来的请求的呢?他是怎么让我们自己的代码去执行这个请求的呢?
好奇了这么多天,今天终于真相大白了。
还在一味的背SpringMVC的那几步原理吗?背来背去发现都不知道自己在背什么,本章带你了解SpringMVC内部执行过程,让springMVC原理永远的刻在你里,化成灰也认识他。
文章共分为四节,只是对SpringMVC的原理及源码部分进行了简单的挖掘不是很深入,通过这几节可以了解下SpringMVC的原理及源码部分也可以领略是如何读框架源码。
SpringMVC执行过程内部分析:
先来看下springmvc官方文档中提供的执行过程图:
通过此图可以看出每次都是由前端控制器接收请求进行统一调度,负责找到要执行的控制器方法,这个前端控制器就是springmvc的核心控制器:DispatcherServlet。
再来看下我准备的springmvc执行流程图:
首先在工程中找到这个核心控制器DispatcherServlet类
通过它的父级一步步的往上翻就可以看出,这个核心控制器其本质就是一个servlet,所以我把SpringMVC理解为是对原始的Servlet进行了封装和改造。
断点跟踪:
通过断点一步步跟踪源码中的执行流程。
第一步:
首先在DispatcherServlet类中找到doService方法,因为核心控制器中首先是执行这个方法。在这个方法中停留一个断点会发现前端发起请求后会在这里拦住。
在doService方法中都是在向请求域中存一些数据。
在该方法的下面会发现继续调用本类中的doDispatch方法。
第二步:
在doDispatch方法中会拿到处理器映射器(handlerMapping),根据请求地址从处理器映射器中拿到要执行的handler(编写的控制层方法)。
此时,就可以看出handle指向的是我们本次请求的控制层中的那个方法。
第三步:
在doDispatch中会拿到处理器适配器(HandlerAdapter),并按照特定规则(HandlerAdapter要求的规则)去执行Handler。
继续往下走,会发现在该方法中通过适配器调用处理器hanler:
此时,因为这个ha代表的是一个接口,需要找到这个handle具体是由那个类执行的,通过debug下面的变量可以看出此时的handle是由ha中的那个实现类去执行的。
可以看出本次ha代表的是RequestMappingHandlerAdapter这个类。那么我们再去这个类中找handle方法的时候发现没有。
这就有点奇怪了,这是怎么回事呢?
来看:
在他的父类中有一个handle方法,并且调用了本类中的抽象方法handleInternal。这是什么骚操作?
其实它是这样做的:
handle是在接口中定义,然后通过一个抽象类去实现这个接口,并重写接口中的handle方法,然后让重写后的方法调用本类的抽象方法。(此时该抽象类就可以让很多子类继承重写抽象方法每个都处理具体的逻辑)再让具体的处理类继承该抽象类重写其中的抽象方法,对外公开调用时还是接口中的handle方法。
这样做的意义是:把接口中重要的方法实现出来让某一个类单独的去处理具体的方法以达到各司其职的目的。
第四步:
所以我们需要在RequestMappingHandlerAdapter类中找到handleInternal方法,就相当于执行了handle方法。
发现在该方法中它又继续调用了本类中的invokeHandlerMethod方法,从方法名就可以看出这个方法是执行控制器方法。
在invokeHandlerMethod方法中中间大部分都是赋值,那么当走到调用invokeAndHandle方法的时候,观察下面的debug变量就会发现此次请求的各种信息:
走到这拿到具体的执行类和方法及其他信息后,就会通过反射的方式调用指定的方法去执行此次请求;
第五步:
找到invokeAndHandle方法,继续跟踪:
这里先找到调用的invokeForRequest方法继续跟踪:
这里它首先调用了一个getMethodArgumentValues方法,该方法的作用是获取此次请求执行所需的参数,我演示的这个请求没有参数先跳过,继续找到它这里调用本类的doInvoke方法。
这个方法的作用是拿到此次请求的参数后去执行具体的控制层中方法。
该方法是要拿到要执行的方法调用invoke去执行,将执行后的结果再逐步返回到ServletInvocableHandlerMethod类中的invokeAndHandle方法去处理返回的结果。(如果在这里将debug放行后就会走到我们自己的控制层断点中)。
到这里可能还有点疑问,就是核心控制器到底是怎么通过我们请求的路径找到具体的控制层中的方法的呢?
在执行initStrategies方法(初始化映射器、适配器等),初始化映射器时解析定义在方法上的请求路径,并建立和方法的对应关系。
在拿到请求的时候截取此次请求中的路径并通过截取得到的路径找到与之对应的方法, 通过反射的方式调用具体的方法让其执行。
说明:SpringMVC执行initStrategies方法进行初始化的时候有两种方式:
1、在web.xml中配置了1 的时候工程启动的过程中就会执行,故工程启动时就会初始化处理器映射器、适配器等从而加载解析定义的所有的控制器方法及请求地址。
2、如果没有配置这个那么工程在启动完成之后核心控制器处理第一个请求的时候才会执行该方法之后的请求就不会再执行该方法了。
那么它到底是怎么加载解析的呢?下节继续说这块。
总结:
首先工程启动或第一次请求的时候就会加载解析定义的请求路径并建立和方法的绑定关系,接收到请求后通过请求地址中的路径找到具体的方法让其执行;
其次,每次请求都会被springmvc的核心处理器(DispatcherServlet)接收到,首先会执行dispatcherServlet中的doService方法向请求域中存一些数据后继续调用本类中的doDispatch方法,在doDispatch方法中会根据映射器拿到处理器(handler)后再拿到适配器(handleAdapter)。在拿到这两个后会调用适配器去执行处理器,HandlerAdapter接口中的handle方法(不同的处理器会调用适配器下不同的具体执行类,使用注解定义的控制器会执行RequestMappingHandlerAdapter类中的handleInternal方法),在handleInternal方法中接着继续调用本类中的invokeHandlerMethod方法,走到这个方法的时候就会发现此次请求的执行类、方法及其他信息。
在这个方法拿到这些信息后继续调用invokeAndHandle去执行这个方法,这个方法才是真正去执行具体的控制器方法,在这个方法中调用invokeForRequest去执行,invokeForRequest方法中首先会通过getMethodArgumentValues方法去获得此次请求的参数数据后调用doInvoke去执行具体的控制器方法。这些全部执行完后会返回到invokeAndHandle方法中通过调用handleReturnValue来处理本次请求的结果(视图解析器也是在这之后执行的)。
以上就是本次了解springmvc执行过程的全部内容,可能过于片面或浅薄。了解大概执行流程后有兴趣的童鞋还可继续往下深究,把springmvc翻个底朝天。
公众号:沉默木头人
优快云:沉默木头人(ID:qq_44322555)
喜欢感兴趣长按下面二维码关注吧!
原创不易,不喜勿喷,如果能够帮助到你或对你有所启发欢迎下方留言。
喜欢就开始你无情的三连击:点赞、分享、关注。这将是我写作更多有趣有益有知的好文章的动力;