SpringBoot源码——请求全过程源码分析——⼀步⼀步详细分析
⽂章⽬录
前⾔
作为java开发,经常使⽤SpringBoot框架,那么掌握SpringBoot的请求的全流程还是⼗分必要的。
没有研究源码之前,有⼀些疑问:
为什么要返回json串的时候需要在⽅法上加@ResponseBody,或者在类上加@RestController
为什么返回String类型,不加@ResponseBody,就是要去视图解析器
返回modelAndVIew类型并加@ResponseBody,那返回的json串,还是页⾯呢
视图解析器可以有多个吗,如何选择的?加了@ResponseBody之后,还会经过视图解析器处理吗
为什么有时候使⽤了fastjson,最好也需要加fastjson的消息处理器HttpMessageConverters
若加上⽅法上加@RequestMapping("/index.html"),且resoure⽬录下的static⽬录也有⼀个index.html页⾯会怎么样
其实还有很多其他疑问,为什么会这样,其实很重要的⼀点就是,现在的开发推荐:约定⼤于配置,配置⼤于编码。
很多东西已经约定好的,直接告诉我们就这样⼦,按照这样写就对了,或者很多starter的使⽤,只要在yaml配置⼀下就可以了,对于编码的要求较少。那么因为框架或者⼯具等编码较少,我们就不知道这些框架本来的逻辑。有疑问就很⾃然了。
⼀、SpringBoot源码
SpringBoot源码分析是⼗分必要的,包括但不限于
包括启动类上的注解@SpringBootApplication就是⼀个很重要的知识点
另外main⽅法的运⾏之后,源码⾥new SpringApplication(primarySources).run(args); ,这⾥有2个很重要的操作,分别是new和run,分析⾥⾯的源码可以看到spring.factories⽂件⾥⾃动配置类加载,各种解析器,,bean的定义,加载和创建等操作。
还有就是⼀个前端的get/post请求到controller⽅法经历了什么。
这次我就先分析第三个,看看⼀次请求经历了什么。
另外⼀篇⽂字是基本分析,有兴趣可以看看
⼆、代码准备
1、请求
下⾯以返回json格式为例
get请求⼊参
返回参数
{
“age”: 1,
“name”: “demoData”
}
2、controller层
@Controller
@RequestMapping("/spring")
@Slf4j
public class SpringController {
@GetMapping("/dataBinder")
@ApiOperation(value ="我是⼀个可爱的测试获取dataBinder的接⼝")
@ResponseBody
public DataBinder dataBinder(DataBinder dataBinder){
return dataBinder;
}
}
@Data
class DataBinder {
String name;
Integer age;
}
3、分析起点
分析开始点org.springframework.web.servlet.DispatcherServlet#doDispatch
该⽅法protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception是请求的必经之路。因为DispatcherServlet本⾝就⼀个servlet,它配置的匹配映射地址是 / 。在springboot中它是默认配置好了,在springmvc中,我们可以在l⽂件中看到它的配置如下:
<servlet>
<!-- 配置DispatcherServlet -->
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定spring mvc配置⽂件位置不指定使⽤默认情况 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:l</param-value>
</init-param>
<!-- 设置启动顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- ServLet 匹配映射 -->
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
由于DispatcherServlet是⼀个特殊的Servlet,⼀般Servlet 调⽤ service() ⽅法来处理客户端的请求,⽽DispatcherServlet在service()⽅法⾥,经过⼀些步骤之后,会调⽤doDispatch()⽅法。
因此我们从doDispatch开始⼊⼿分析。
三、源码分析
下⾯以返回json格式为例。若分析返回其他类型,⽐如modelAndView或者静态⽂件等我们在后⾯再分析。
【始】
2020-09-30
mappedHandler = getHandler(processedRequest);这⾥是获取HandlerExecutionChain处理器执⾏链,⾥⾯包含处代表具体处理逻辑的⽅
法HandlerMethod对象和若⼲个集合(包括spring⾃带的和⾃⼰⼿动添加的)。
进⼊getHandler⽅法org.springframework.web.servlet.DispatcherServlet#getHandler:
我们可以看到this.handlerMappings就是执⾏路径映射的⼀个处理器,这⾥有5个,它可以根据请求的路径/spring/dataBinder得到真正的处理者HandlerMethod对象。
⽐如第⼀个是requestMappingHandlerMapping,它可以根据controller类的⽅法路径进⾏映射。
还有最后⼀个SimpleUrlHandlerMapping,就是处理路径到resource⽬录⾥⾯静态⽂件的。⽐如访问为localhost:8080/index.html,访问⼀个页⾯。
⽬前我们进⼊第⼀个HandlerMapping的getHandler()⽅法org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
可以看到第⼀句Object handler = getHandlerInternal(request);,这个就是获取⼀个标识真实的处理⽅法的对象HandlerMethod。
进⼊该⽅法,继续跟代码会到org.springframework.web.hod.RequestMappingInfoHandlerMapping#getHandlerInternal
⾥⾯有⼀个HandlerInternal(request);,
继续跟会到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
如下图所⽰:
第⼀⾏String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);获取请求路径名为/spring/dataBinder。
红⾊框框⾥⾯的HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);就是进⼀步去获取真正的处理⽅法HandlerMethod
继续进⼊该⽅法lookupHandlerMethod,org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod
截图如下
第2⾏List<T> directPathMatches = MappingsByUrl(lookupPath);,就是从箭头的12个路径中有没有与请求路
径/spring/dataBinder⼀致的,可以看到第8个就是⼀致的。那么现在就获取到了⼀个该路径的⼀个list,list也只有⼀个元素,因为只有第8个匹配到了。
在经过addMatchingMappings(directPathMatches, matches, request);,该⽅法就是去正在处理的⽅
法,到之后就能获取到matches的list,⾥⾯也是⼀个元素,Match也就是包含了handlerMethod的⼀个类⽽已。
然后该⽅法最后⼏⾏,根据bestMatch.handlerMethod;就得到真正的处理⽅法。
springboot aop现在已经获取到了handlerMethod,那就开始返回
返回到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal
⾥⾯有return (handlerMethod != null ? ateWithResolvedBean() : null);即不为空,那就执⾏⼀个
到这⾥第⼀⾏就完成了,Object handler就是com.jd.ins.qsm.ller.SpringController#dataBinder(DataBinder),真正的⽅法处理器。
接下⾥该⽅法中间断点处的代码HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);这个是获取HandlerExecutionChain执⾏链的⽅法。
截图如下:
第⼀句,就是问Object (handlerMethod)这个是不是HandlerExecutionChain执⾏链这个类型,属于就转换成执⾏链,显然不是,那就new⼀个,并设置handlerMethod属性。
接下⾥⼀个for循环,获取的所有的,然后加⼊到⽅法执⾏链中,包括我⾃⼰⾃定义的testInterceptor。然后返回处理器执⾏链。
由于第⼀个requestMappingHandlerMapping已经能够处理,并返回了不为空的处理器执⾏链,那这⾥也就直接返回该执⾏链到最开始的⽅法doDispatch()⽅法的步骤
mappedHandler = getHandler(processedRequest);
此时这个执⾏链就包含了⼀个真正的处理⽅法HandlerMethod,还有3个,其中⼀个是我⾃定义的⼀个TestInterceptor。
⾄此,第⼀步获取处理器执⾏链结束。
第⼀个步骤,就是获取HandlerExecutionChain处理器执⾏链。
通过多个handlerMapping处理器映射器去寻真正能够处理请求路径的地⽅,⽐如第⼀个handlerMapping就是
requestMappingHandlerMapping,通过它可以寻到标注了@requestMapping等注解的⽅法,并根据请求路径看有没有与之对应的⽅法路径,若正好有,那就返回⼀个HandlerMethod。同时这个执⾏链还会包含多个(若有)。**
恭喜你能看到第⼀个步骤结束,其实你会发现不是很难,加油~~~
【续】
2020-10-17
Handler());就是获取真正的处理⽅法handlerMethod的⼀个适配器的类HandlerAdapter。它对真正⽅法的执⾏做了很多的⽀持⼯作。
我们看到this.handlerAdapters有4个适配器,其中第⼀个就是requestMappingHandlerAdapter,然后看它⽀不⽀持我们的真正的处理⽅法handlerMethod。
很显然,我们的handler本来就是handlerMethod,所以肯定返回true。
也就是这个requestMappingHandlerAdapter这个适配器能够⽀持handlerMethod,那么就返回了4个adapter中的第⼀个requestMappingHandlerAdapter。
从上⾯的截图中,可以看到这个适配器requestMappingHandlerAdapter还是有很多属性的。这些属性对真正处理⽅法执⾏的过程的之前或者之后做了⼀些⼯作。
其中⾥⾯蓝⾊框框的属性returnValueHandler和messagConverters⽐较重要,后期我们会讲到这个2个的作⽤。
⾄此,第⼆步获取处理器适配器结束。
第⼆个步骤,就是获取处理器适配器HandlerAdapter。
通过多个HandlerAdapter看它们能否⽀持真正处理⽅法,⽐如第⼀个handlerMapping就是requestMappingHandlerAdapter,它就刚好能⽀持 处理器⽅法类型 为HandlerMethod的。
恭喜你能看到第⼆个步骤结束,加油~~~
3.applyPreHandle()⽅法
mappedHandler.applyPreHandle(processedRequest, response),执⾏执⾏链中的的pre⽅法,这个很好理解,按照的先后顺序先后执⾏pre⽅法。
可以看到,有三个,其中第⼀个就是我们⾃定义的,继续执⾏到我们⾃定义的pre⽅法中,
可以看到,我们⾥⾯就打印了⼀句话⽽已,并且返回true。
返回true之后,这是这个⽅法interceptor.preHandle(request, response, this.handler)就是true,加⼊!,那就是false,也就不会执⾏if⾥⾯的逻辑。
接下来就都依次的执⾏其他的pre⽅法。
若有某⼀个pre⽅法返回了false,那么这⾥取反之后就是true,那就会执⾏if⾥⾯的⽅法,那样也会执⾏
的triggerAfterCompletion⽅法,这个⽅法的⼯作可以是处理⼀些收尾⼯作。并返回false到最上层⽅法。
最上⾯的这⼀层,也是取反判断。
若⼀切顺利,即所有的pre⽅法都返回的true,则不会执⾏上⾯截图中if⾥⾯的return,所以若哪个pre⽅法返回了false,那这⾥也就执⾏了return,整个请求过程到这⾥就提前GameOver了。
从这⾥可以看出,pre⽅法可以进⾏鉴权,⽇志等操作。
第三步,执⾏的pre⽅法。
若所有的pre⽅法都返回true,则请求会继续,若有⼀个返回了false,则整个请求结束
恭喜你能看到第三个步骤结束,加油~~~
【续】
2020-10-19
4.ha.handle()⽅法
mv = ha.handle(processedRequest, response, Handler());,该⽅法的主要作⽤就是执⾏真实⽅法逻辑,并返回modelAndView对象。
【续】2021-01-24
该操作是整个请求中最重要的⼀个步骤,⾥⾯从请求流程的⾓度,可以⼤概分为5个步骤:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论