SpringBoot中的Controller注册
本篇将会以Servlet为切入点,通过源码来看web容器中的Controller是如何注册到HandlerMapping中。请求来了之后,web容器是如何根据请求路径找到对应的Controller方法并执行的。
先讲下本文的大概思路和流程图:
- 我们经常使用的
RequestMapping
这个注解对应的方法最终会被RequestMappingHandlerMapping
处理,并封装成一个HandlerMethod
注入到自己内部的mappingRegistry
容器中。这一步是Controller的注册,被执行的触发点是因为RequestMappingHandlerMapping
这个类实现了InitializingBean
接口,由Spring容器触发。 - tomcat容器被启动的时候,最后会调用Servlet的init方法,这里会把所有的
HandlerMapping
注册到自己内部的handlerMappings
属性中。这样Servlet和RequestMapping注解的Controller就建立起了间接关系。 - 当请求到来的时候,tomcat拿到并封装好请求体后会调用Servlet的service方法。这个方法最终会走到
DispatcherServlet
的doDispatch
方法,这个方法中会找到最适合的HandlerMapping
并取出对应的HadlerMethod
,然后给对应的HandlerAdapter
执行. - controller注册流程图
- controller发现和使用流程图
正文开始
处理请求的DispatcherServlet
Servlet接口的源码
1 | java复制代码public interface Servlet { |
springboot内置了tomcat容器,而tomcat容器是遵循了servlet规范的。servlet规范中定义了初始化、响应、获取配置信息和销毁时回调钩子。从servlet的规范中可以看出,tomcat启动时会调用servlet的init方法,处理请求时会调用service方法,容器销毁时会调用destroy方法。servlet中最核心的实现就是我们所熟知的DispatchServlet,看下DispatchServlet的继承体系
从DispatchServlet的继承体系中,看下Servlet的初始化做了什么。
Servlet的初始化 init
HttpServletBean中的init方法源码
1 | java复制代码@Override |
从HttpServletBean中的init方法可以看到,这里核心的就是设置了Servlet的一些
bean properties,继续到子类
FrameworkServlet中看initServletBean
方法
1 | java复制代码@Override |
接着跟进DispatchServlet的onRefresh
方法,这个方法中会初始化DispatchServlet的九大策略,这里我们只关心initHandlerMappings
方法
1 | java复制代码@Override |
核心看下initHandlerMappings
方法
1 | java复制代码private void initHandlerMappings(ApplicationContext context) { |
看下默认的 HandlerMapping 有哪些
这里我们只关心RequestMappingHandlerMapping
这个类,这个类就是处理我们Controller上的RequestMapping注解的类。
注意这里的handlerMappings
,后面处理请求的时候,会从handlerMappings中选择一个最合适的HandlerMapping来处理请求
Servlet的请求处理 service
HttpServlet中的service方法源码
1 | java复制代码/** |
DispatchServlet的doService方法
1 | java复制代码@Override |
DispatchServlet的doDispatch方法
1 | java复制代码protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { |
这里可以记录Servlet处理请求的调用链 service -> doGet -> processRequest -> doService -> doDispatch
RequestMappingHandlerMapping 做了啥
从上面的继承图可以看出RequestMappingHandlerMapping实现了InitializingBean接口,所以初始化的时候会调用afterPropertiesSet方法。
1 | java复制代码@Override |
接着看父类AbstractHandlerMethodMapping的afterPropertiesSet方法
1 | java复制代码@Override |
RequestMapping转换为RequestMappingInfo
1 | java复制代码@Override |
这个方法会把方法上的RequestMapping转换为RequestMappingInfo,把类上的RequestMapping转换为RequestMappingInfo,然后再把两个RequestMappingInfo合并成一个(url的合并)。
HandlerMethod的注册
1 | java复制代码protected void registerHandlerMethod(Object handler, Method method, T mapping) { |
这里需要注意的是,注册的Controller是直接注册的HandlerMethod,这个HandlerMethod就是对应的Controller类中具体请求对应的方法,这个对象封装了所有信息,后面获取出HandlerMethod后会通过反射调用具体的方法
进入RequestMappingHandlerMapping的getHandler
方法看下,这个方法在父类AbstractHandlerMapping中实现,这里用到了设计模式中的模版方法。
1 | java复制代码@Override |
这里核心关注两个方法,一个是获取处理器的getHandlerInternal
方法,一个是获取对应拦截器链的getHandlerExecutionChain
方法
AbstractHandlerMethodMapping的getHandlerInternal方法
1 | java复制代码@Override |
从上面的方法中可以看出,最后是从mappingRegistry属性中取出的HandlerMethod,mappingRegistry在上面的RequestMappingHandlerMapping中有详细讲解
AbstractHandlerMapping的getHandlerExecutionChain方法
1 | java复制代码protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { |
到这就已经拿到了对应的拦截器链和响应请求对应的方法了。接下来就是调用方法了,这里就轮到HandlerAdapter出场了,如何获取RequestMappingHandlerAdapter的方法getHandlerAdapter
这里就跳过了
再回到DispatchServlet的doDispatch方法中的
1 | java复制代码//这里的ha就是RequestMappingHandlerAdapter类 |
RequestMappingHandlerAdapter类的handleInternal方法
1 | java复制代码@Override |
到这里,整个调用的过程就已经到此为止了。其中的HandlerAdapter
的注册、获取、处理请求反射调用HandlerMethod等以后的章节再分析。
本文转载自: 掘金