由于疫情的原因,今年的清明节实属凄凉,让我们为负重前行的英雄们,默哀。。。。

一直在用Spring,但是从来没考虑过前端的请求是如何调用到Controller中的方法的,只在网上看看别人的回答,今天呢,游戏也不让玩,闲来无事翻了翻Spring请求的源码,在此做个小总结:

网图整楼

spring 单请求 spring 请求过程_spring 单请求

作为一名优秀的JAVA开发工程师,对这张图大家应该都不陌生:首先前端控制器接收请求,然后前端控制器调用处理器映射器…

朗朗上口,倒背如流。但是真要问知其所以然,恐怕大家都一脸懵逼。例如,前端控制器如何拦截请求?懵了吧,来,和我一起读读源码!

前端控制器DispatcherServlet是如何进行加载的?

第一步,我们先找到前端控制器(DispatherServlet),这里我拿到了他的所有父类,看图:

spring 单请求 spring 请求过程_java_02


好,既然他是个Servlet,那么在系统启动时,必然会走init初始化方法,那么它会走哪个类中的init方法呢?我们一一从上往下找:

spring 单请求 spring 请求过程_子类_03

spring 单请求 spring 请求过程_spring 单请求_04


HttpServlet中没有重写init方法;

spring 单请求 spring 请求过程_注解方式_05


显然,上面的类的init方法都是为空的,说明都需要子类来重写,直到HttpServletBean才开始重写init方法。第二步:在HttpServletBean中打断点,如上图;

第三步:我们启动项目,断点顺利停住请求,说明我们的思路没有错误。

spring 单请求 spring 请求过程_JAVA_06


于是我们看看当前方法里面走了啥,它主要走的是initServletBean()方法,当前类的该方法为空,那我们看它的子类FrameworkServlet所实现的initServletBean方法!

spring 单请求 spring 请求过程_java_07

FrameworkServlet所实现的initServletBean方法主要走了两个方法,分别是:initWebApplicationContext()和initFrameworkServlet(),我们一一来看:首先initWebApplicationContext()

spring 单请求 spring 请求过程_注解方式_08


继续往下走,看他还做了什么,我们发现他走了onRefresh()方法:

spring 单请求 spring 请求过程_spring 单请求_09


那么这个onRefresh()方法又走向什么呢?

spring 单请求 spring 请求过程_java_10


当前类中方法为空,说明需要子类来实现,由此,我们就进入到了前端控制器

spring 单请求 spring 请求过程_注解方式_11


这些方法名称看着眼熟不?

spring 单请求 spring 请求过程_子类_12

用户发送一个请求,如何找到对应的controller(执行流程)?

刚刚展现的是DispatcherServlet在项目启动时,会调用它的init方法,有且仅调用一次,那么在接收一个请求时会走的时service方法!(PS:servlet基础知识)

spring 单请求 spring 请求过程_注解方式_13


spring 单请求 spring 请求过程_子类_14


spring 单请求 spring 请求过程_java_15


由此可见,service方法最终还是根据请求方式的不同,被分发到不同的doGet、doPost…方法中;

spring 单请求 spring 请求过程_子类_16


spring 单请求 spring 请求过程_JAVA_17


spring 单请求 spring 请求过程_子类_18


spring 单请求 spring 请求过程_子类_19


spring 单请求 spring 请求过程_子类_20


spring 单请求 spring 请求过程_spring 单请求_21


spring 单请求 spring 请求过程_JAVA_22


spring 单请求 spring 请求过程_注解方式_23


spring 单请求 spring 请求过程_JAVA_24


spring 单请求 spring 请求过程_注解方式_25


PS:这里注意一下,由于我们请求的方式是以@RequestMapping("/test.do")的注解方式,所以我们这里返回的也是一个对应的方法,如果是另外的基于bean名称的访问方式,那么放回的是对应的类!

spring 单请求 spring 请求过程_注解方式_26


spring 单请求 spring 请求过程_JAVA_27


spring 单请求 spring 请求过程_java_28


spring 单请求 spring 请求过程_java_29


spring 单请求 spring 请求过程_子类_30


spring 单请求 spring 请求过程_JAVA_31


spring 单请求 spring 请求过程_spring 单请求_32


spring 单请求 spring 请求过程_spring 单请求_33


PS:走到这里,我很奇怪,怎么也进不去Controller层。讲道理,Handler应该有个入口方法,会去调用Controller层对应的方法。试了好久都没找到入口。于是我用上了前面的基于Bean名称的方式。新建一个Bean:

spring 单请求 spring 请求过程_java_34


浏览器访问:

spring 单请求 spring 请求过程_注解方式_35


直接来到处理器适配器调用处理器的位置:

spring 单请求 spring 请求过程_JAVA_36


进去,发现和上面那种方式的方法不一样:

spring 单请求 spring 请求过程_java_37


然后,再点进去,惊喜:

spring 单请求 spring 请求过程_spring 单请求_38


顺利读到了Controller层!然后可以执行项目中的方法。。。