(1)用户发送请求至前端控制器DispatcherServlet;
(2) DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet;
(4)DispatcherServlet 调用 HandlerAdapter处理器适配器;
(5)HandlerAdapter 经过适配调用 具体处理器(Handler,也叫后端控制器);
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回具体View;
(10)DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
(11)DispatcherServlet响应用户。
SpringMVC如何找到请求的目标控制器方法?
Spring MVC Web应用开发时,我们会开发出相应的 controller class/method, 关联到相应的url,然后用户在访问系统时,输入正确的url,相应的controller class/method 就会被执行,返回给用户相应的处理结果。那么Spring MVC Web应用内部是如何找到一个请求对应的controller class/method方法呢 ? 本文基于源代码回答此问题。
简单地讲,应用在启动的时候,Spring MVC将配置文件或者通过注解@Controller+@RequestMapping定义的<url,controller class/method>映射关系搜集起来,保存在内存中,然后在请求到达时,根据请求的url(实际上也会使用相应的HTTP method)就能找到相应的 controller class/method 。下面我们分成这两步进行分析。
初始化handlerMappings
DispatcherServlet
有一个属性handlerMappings
,用于在内存中保持配置定义的<url,controller class/method>
映射关系。在DispatcherServlet
初始化的过程中,该属性会根据配置信息被填充正确的信息。
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// detectAllHandlerMappings 表示是否要从上下文中检测所有的 handlerMappings,缺省值为 true
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 从 ApplicationContext 及其祖先级别上下文中找出所有类型为 HandlerMapping 的 bean ,
// 这些 bean 实在 bean 扫描阶段根据配置或者注解进入到 Spring IoC 容器的。
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
// 对搜集到的 handlerMappings 进行排序
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
找到当前请求的handler
每个请求到达DispatcherServlet被处理时都会使用其方法protected void doDispatch(HttpServletRequest request, HttpServletResponse response)。该方法调用getHandler()来获取当前请求的handler。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 遍历所有的 handlerMappings , 找到第一个支持当前请求 request 的那一个并返回一个 HandlerExecutionChain
// 对象,注意这里是 HandlerExecutionChain 对象,而不是一个什么 handler 对象,
// 一个 HandlerExecutionChain 的内容是 1 handler + N handler interceptors , 包含了相应的 handler 对象
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
// 如果当前 HandlerMapping hm 支持当前 request, 那就直接使用它;
// 如果当前 HandlerMapping hm 不支持当前 request, 其 getHandler 方法会返回 null;
// getHandler 内部会获取 request 的 url 信息 , http method 信息,然后判断自己是否支持该 request,
// 具体的实现逻辑不同的HandlerMapping实现类会有不同。
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
// 如果没有所有配置的 handlerMappings 都不支持该请求,则返回 null,
// 此时DispatcherServlet会抛出一个异常NoHandlerFoundException或者向客户端返回一个404,
// 具体怎么做看开关的设置,缺省是会向客户端返回一个404。
return null;
}