上篇文章《SpringMVC详解-怎么接收请求》已经提到过了,当Servlet接收到请求后会最终调用doDispatch方法(这里省略部分代码保留主要功能):
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1.确定使用的handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 确定handler的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 拦截器前置调用
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际调用
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 设置默认视图名
applyDefaultViewName(processedRequest, mv);
// 拦截器后置调用
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 分发结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
```
## 1 处理器 HandlerMapping
请求分发完毕后就需要去进行处理,这就是我们本文所要讲的重点:HandlerMapping。如果使用过springmvc进行开发过的同学都知道,handler通常指的就是业务上我们在Controller层中编写的带有@RequestMapping的方法,在SpringBoot中为了方便开发,框架还提供了@GetMapping,@PostMaping等注解来简化配置。而这些所有方法为构成一个HandlerMapping。
## 2 如何转化HandlerMapping?
我们目光可以聚焦到EnableWebMvcConfiguration#requestMappingHandlerMapping方法,它会去创建RequestMappingHandlerMapping对象(就是转化上面提到注解的类)。这个对象实现了InitializingBean接口,所以最终会调用其afterPropertiesSet方法:
```java
@Override
public void afterPropertiesSet() {
// 初始化handler
initHandlerMethods();
}
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
其中的processCandidateBean会去解析带有@Controller注解的类。我们简单的看一下代码:
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
isHandler方法会找到带有@Controller注解或@RequestMapping的类,detectHandlerMethods方法会对整个类进行处理,解析带有@RequestMapping注解的方法并注册到MappingRegistry中去。
3 如何注册HandlerMapping?
最后看一下框架是怎么将handlers注册到控制器DispatcherServlet中去的。
DispatcherServlet的父类中添加了一个ContextRefreshListener监听器,当容器完全启动后,会调用其onRefresh方法,而正是这个方法,去注册了handers。
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
// 注册handler
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
可以看到控制器初始化了非常多的东西,这里我们只关注initHandlerMappings方法,后面的文章会讲到
initHandlerAdapters,initHandlerExceptionResolvers等。
具体代码如下(省略部分代码):
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) { // true
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
this.handlerMappings = new ArrayList<>(matchingBeans.values());
}
else {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
由以上代码可以知道,它会去Spring IOC容器中获取HandlerMapping类型的所有Bean设置为控制器的Handler,而上文说的RequestMappingHandlerMapping就是其中一个Bean。与此同时我们发现,也可以通过自己手写一个HandlerMapping实现类从而注册到控制器当中去。
最后用流程图总结一下这个过程:
image.png
网友评论