前言
我们都知道,在一个请求被前端控制器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)
至于案例,以后有空了再补上
