前言
我们都知道,在一个请求被前端控制器DispatchServlet
捕获后会经历下面几个流程:
DispatherServlet
根据请求URL解析获取请求URI,调用HandlerMapping#getHandler
方法获取HandlerExecutionChain
- 获取返回的
HandlerExecutionChain
处理器执行链(包括处理器对象和拦截器对象) - 根据处理器执行链获取一个处理器适配器
HandlerAdapter
,如果成功获取,则开始执行拦截器) - 处理器适配器根据请求的
Handler
(一般来说是HandlerMethod
)适配并根据配置的HttpMessageConveter
将请求消息解析为模型数据,填充Handler
入参,开始执行处理器逻辑。 - 处理器执行完毕,返回
ModelAndView
,处理器适配器接收到后返回给DispatherServlet
DispatherServlet
根据模型和视图请求对应的视图解析器- 视图解析器解析模型数据获取对应的视图,渲染视图后返回给
DispatherServlet
DispatherServlet
将渲染后的视图相应给用户或客户端
那么Spring容器启动后是如何自动发现处理器(我在这称之为自动发现机制)并进行注册的呢?
在web环境下Spring容器启动时会注册
HandlerMapping
,具体在SpringBoot中,WebMvcAutoConfiguration
会通过WebMvcConfigurationSupport#requestMappingHandlerMapping
方法向容器中注册一个HandlerMapping
的实现RequestMappingHandlerMapping
自动发现机制的实现
Spring容器启动时会注册HandlerMapping
,在这里我们就以RequestMappingHandlerMapping
实现类为例进行分析
RequestMappingHandlerMapping
RequestMappingHandlerMapping#afterPropertiesSet
方法实际上调用了父类的afterPropertiesSet
的方法:
1 | public void afterPropertiesSet() { |
而在父类的这个方法中进行初始化处理器逻辑:
1 | /** |
在initHandlerMethods
方法中先从容器中获取所有的候选bean,调用processCandidateBean
方法:
1 | /** |
在processCandidateBean
方法中根据bean的类型调用由子类实现的isHandler
方法判断是否是处理器,然后调用detectHandlerMethods
方法寻找该处理器中的处理方法并注册。
来看下RequestMappingHandlerMapping#isHandler
1 | protected boolean isHandler(Class<?> beanType) { |
对应的AnnotatedElementUtils#hasAnnotation
方法,最终会调用到AnnotatedElementUtils#searchWithFindSemantics
方法,代码片段如下
1 | else if (element instanceof Class) { |
发现这个地方查找是否有指定注解时,如果继承的类或实现的接口有相应的注解也是可以的,这个特性在某些情况下是很有用的,比如在自动配置类中可以利用这一特点,使用@Bean
注解再配合@Conditionalxxx
等注解实现按需注入Controller。我们只要定义一个标记类/接口,再在该类上注解@Controller
或@RequestMapping
,然后让需要按需注入的Controller继承这个标记类/接口即可。
注意,在一般情况下使用
@Bean
或@Component
注解是不能将一个Bean注册为Controller的
AbstractHandlerMethodMapping#detectHandlerMethods
现在我们来看下这个自动发现机制中最关键的方法:
1 | protected void detectHandlerMethods(Object handler) { |
而在RequestMappingHandlerMapping#getMappingForMethod
中先检查是否注解了@RequestMapping
,如果有则获取注解中的请求的路径,请求的方式(GET,POST,…)等等信息,封装到RequestMappingInfo
中返回,具体的代码就不放了,还是很好理解的。
再来看下最后这个AbstractHandlerMethodMapping#registerHandlerMethod
方法
1 | protected void registerHandlerMethod(Object handler, Method method, T mapping) { |
很好理解,调用mappingRegistry#register
方法进行注册,那这个mappingRegistry
是什么东西呢?
MappingRegistry
实际上就是AbstractHandlerMethodMapping
类中的一个内部类,看名字很好理解:映射注册表,这个类中维护了存放映射信息的map
MappingRegistry
MappingRegistry
是AbstractHandlerMethodMapping
类中的一个内部类,在其中维护了五个用于存放映射信息的Map:
1 | // 真正意义上的注册表,以RequestMappingInfo为key |
MappingRegistry#register
1 | public void register(T mapping, Object handler, Method method) { |
这个方法说白了,就是把处理器,处理器方法,请求映射信息等等信息放到Map中,看到这里可能会对RequestMappingInfo
和 MappingRegistration
这两个东西感到疑惑,其实很简单,前者是Spring容器启动时控制器自动发现机制根据方法上@RequestMapping
注解中的信息封装的对象,决定了什么样的请求能命中那个处理器方法,这也是MappingRegistry#registry
注册表中以RequestMappingInfo
为key的原因,而MappingRegistration
中封装了处理器Handler
,处理器方法HandlerMethod
信息。
所以现在能知道一个请求到来时是如何找到处理器方法并调用的了:
根据请求的url到
MappingRegistry#urlLookup
字段中匹配,如果匹配上,则取出对应的RequestMappingInfo
,再到MappingRegistry#registry
字段中取出MappingRegistration
,再取出MappingRegistration
中的处理器Handler
和方法HandlerMethod
,反射调用完成请求。
当然,这只是大概的流程。
后语
Spring容器启动,注册处理器的流程分析完了。其实说白了,Spring容器注册控制器就是扫描容器中的bean然后检查是否是控制器Controller,然后将其中注解有@RequestMapping
的方法注册为处理器方法。
其实HandlerMapping
的实现类并不是只有RequestMappingHandlerMapping
一个,但是是AbstractHandlerMethodMapping
提供的主要的实现逻辑,而实现类只是提供了基础的判断:是否是处理器(isHandler
),获取请求映射信息getMappingForMethod
等抽象方法,所以说如果在某些场景下需要实现自定义的HandlerMapping
时我们可以通过继承RequestMappingInfoHandlerMapping
然后重写isHandler
和getMappingForMethod
方法即可。(RequestMappingHandlerMapping
继承自RequestMappingInfoHandlerMapping
)
至于案例,以后有空了再补上