根据servlet规范我们知道,当有http请求的时候,会调用到servlet中的service方法,在springMVC中会调用到DispatcherServlet中的service方法。
注:FrameworkServlet是DispatcherServlet的父类,HttpServlet是FrameworkServlet的父类
http请求最终会调到DispatcherServlet中的doDispatch方法。
在调用DispatcherServlet中的doDispatch方法之前,还有一些初始化的操作:
一、URL和handler的映射收集;
二、spring容器完成启动后会发布事件;
一、URL和handle的映射收集:
AbstractHandlerMethodMapping(AbstractHandlerMethodMapping是RequestMappingHandlerMapping的父类)类实现了InitializingBean接口,所以会调到其afterPropertiesSet方法:
代码片段一:
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
.....
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
//如果类上面有@Controller注解或者@RequestMapping注解
if (beanType != null && isHandler(beanType)) {
//建立uri和method的映射关系
detectHandlerMethods(beanName);
}
}
映射关系收集总结:
1、在spring容器当中获取所有的Object类,遍历循环各个类;
2、遍历循环类上所有的方法;
3、寻找有@RequestMapping注解的方法,然后注解里面的内容封装成对象RequestMappingInfo, 类上面的@RequestMapping注解也封装成对象, 然后把方法上面的注解属性结合到类上面的RequestMappingInfo对象中;
4、建立方法对象和RequestMappingInfo的映射关系,保存到methodMap这个LinkedHashMap类型中;
5、遍历LinkedHashMap,创建HandlerMethod对象,建立RequestMappingInfo和HandlerMethod对象的映射关系,建立url和RequestMappingInfo映射关系,接下来判断method上是否有CrossOrigin注解,把注解里面的属性封装成CorsConfiguration,建立handlerMethod和CorsConfiguration的映射,这个是做跨域访问控制的;
说明:
上面的代码建立好uri和method的映射关系(具体看上面的detectHandlerMethods方法),以便在后面的http请求的时候,能够在收集好的集合中根据uri找到对应的method,首先会根据url根据映射关系找到对应的RequestMappingInfo,然后根据RequestMappingInfo找到对应的HandlerMethod(handler),然后根据handler找到handlerAdapter,由handlerAdapter进行反射调用到具体方法中。
二、spring容器完成启动后会发布事件:
在FrameworkServlet中定义了一个内部类,关注了spring容器启动完成后的事件发布
所以spring容器启动完成后最终会调到DispatcherServlet中的initStrategies方法:
初始化handlerMapping和handlerAdapter会获取到WebMvcConfigurationSupport创建的HandlerMapping(用不同的方式建立URL和handler的映射,主流的handler和URL的映射还是RequestMappingHandlerMapping建立的,handler是handlerMethod,其他的handler有的是类本身,比如SimpleUrlHandlerMapping建立的映射)和HandlerAdapter,并将其分别放入handlerMappings和handlerAdapters的list集合。
那么MVC的请求在进入doDispatcher方法后:
代码片段二:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
//异步管理
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//文件上传
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//根据请求找到对应的handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//获取跟HandlerMethod匹配的HandlerAdapter对象
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//前置拦截器,如果为false则直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//调用到Controller具体方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
//中置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
.....
.....
//视图渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
.....
}
doDispatch方法总结:
1、会根据url从之前收集的关系映射集合中找到对应的handler,主流的handler其实就是HandlerMethod,因为大多数都是采用注解@Controller和@RequestMapping的方式定义路径;
2、获取跟HandlerMethod匹配的HandlerAdapter对象;
3、前置拦截器的调用,如果是false则返回;
4、反射调用到Controller具体方法;
5、中置拦截器的调用
6、视图渲染,响应视图
7、后置拦截器(在processDispatchResult方法中)