我们熟知的流程
①②:前端控制器DispatcherServlet(所有的请求都有经过它来统一分发)拦截请求,交由HandlerMapping返回HandlerExecutionChain【包括包含一个Handler处理器对象、多个HandlerInterceptor拦截器】,
HandlerMapping完成了客户请求到Controller之间的映射,只不过到control还需要一些步骤如③④包装一下(加一层花样多一些嘛),有下面几个
BeanNameUrlHandlerMapping
SimpleUrlHandlerMapping
ControllerClassNameHandlerMapping
③④:HandlerExecutionChain包装为适配器(支持多种控制器),然后返回ModelAndView对象【包含了模型(Model)和视图(View)】
⑤:如果视图(View)本身是
- View对象
- 只是逻辑名,还需要经过视图解析器变成View对象
⑥:View根据模型渲染页面
与spring的关系
简单来说就是父子容器的关系,只是每个有些设置不同,关于容器创建可以去看看spring源码,其中最为特殊的就是springMVC会注册ContextListner,之后再finishRefresh中会触发九大组件的完成
multicastEvent 遍历所有的listner ,其中ContextRefreshLisener处理方法会注册设置九大组件
上面就是两个容器的创建,之后就是service方法来接受请求了,其中最重要的就是doDispatch,依靠九大组件来完成请求的处理
重点doDispatch讲解
下面是doDispatch的线型代码流程,我将把每个小框框的作用讲出,串出来就是处理流程
1. 检验是否为文件上传请求
主要根据contentType(是否是 mutipart/ 开头)来判断处理,解析出上传的文件
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
2. HandlerExecutionChain 的寻找
根据HandlerMapping来寻找对应的
- handler(handler就是我们写得处理这个请求逻辑)
- interceptors 拦截器
HandlerExecutionChain mappedHandler = null;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
2.1 handler的寻找
handler的寻找我们需要依靠handlerMapping这个组件
其中有主要的两个HandlerMapping实现,分别是AbstractHandlerMethodMapping,AbstractUrlHandlerMapping,类图如下
了解这个两个类之前需要了解,Controller的3种实现方式,可以参考这个
- 标注@Controller注解,注解指定url
- 继承Controller类,实现handleRequest方法,XML里面指定url
- 继承HttpRequestHandler,实现handleRequest方法,XML指定url
- AbstractUrlHandlerMapping解决有@Controller方式的handler寻找
如何寻找?AbstractUrlHandlerMapping内部维护了MappingRegistry内部有六个集合,主要用的是mappingLookup,保存了从url到方法的映射。
mappingLookup在哪初始化?因为AbstractUrlHandlerMapping继承了InitializingBean这个MappingRegistry集合是在容器创建这个bean时的setProperties方法设置好的
- AbstractHandlerMethodMapping类解决继承Controller和HTTPRequestHandler的Controller
如何寻找?AbstractHandlerMethodMapping内部保存了,Map<PathPattern, Object> handlerMap,保存了从url到controller对象的映射
handlerMap在哪初始化?AbstractHandlerMethodMapping继承了ApplicationContextAware就会在容器创建这个bean时执行invokeAwareMethod
2.2 interceptors的寻找
主要根据handler的url来在adaptedInterceptors匹配,能匹配上的就在chain的interceptor集合中加入。
adaptedInterceptors在哪初始化?同样继承了ApplicationContextAware就会在容器创建这个bean时执行invokeAwareMethod进行初始化
3.获取handler对应handlerAdapter
上述寻找handler的过程,其中每个handler方法名字都不一样,所以加一层适配,使得可以统一调用handle方法,当然adapter远远不止这么简单,因为后面调用就是靠这个类,所以这个类需要具备一个功能,这也是其中最繁琐的实现,参数值的处理解析
。
4.处理last modified
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;
}
}
这个方法可以帮助我们减少获取资源的次数
5. intercetpor的prehandler方法
这里就是我们经常在项目里写的拦截器的prehandler方法执行的地方
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
6. handler对应handlerAdapter调用
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
handler的adapter有很多,下面就拿我们经常使用的RequestMappingHandlerAdapter讲
RequestMappingHandlerAdapter主要处理对于@Controller标注的类,我分为创建和调用,需要注意的是创建实在springMVC容器启动时创建的,这个doDispatch进行了调用,但必须要讲解创建过程才能理解
6.1 创建
对于容器对RequestMappingHandlerAdapter对象的创建,最重要的在于afterPorpertiesSet方法,对下图属性进行了设置
- initBinderAdviceCache:设置全局配置的InitBinder,全局类型的InitBinder需要声明的类上使用@ControllerAdvice进行标注
- modelAttributeAdviceCache::设置全局配置的ModelAttribute,全局类型的ModelAttribute需要声明的类上使用@ControllerAdvice进行标注
- argumentResolvers:用于根据当前handler的方法参数标注的注解类型,如@RequestParam,@ModelAttribute等,获取其对应的ArgumentResolver,以将request中的参数转换为当前方法中对应注解的类型
- returnValueHandlers:通过ReturnValueHandler对返回值进行适配
@InitBinder可以绑定请求参数到指定的属性编辑器,比如
- 把传过来的string进行trim修剪
- 把传来string类型的日期转java Date类型
6.2 调用
调用也有准备过程和调用过程
准备主要准备:initBinder,modelAttribute,参数处理器,返回值处理器等等
6.2.1 准备
可以看出来之前创建RequestMappingHandlerAdapter的设置的值都用上了,但是多了个两个 initBinderCache,modelAttributeCache,这是局部的@InitBinderCache,@ModelAttribute,只在定义的那个类中生效,而不是全局的。这样我们就需要将全局和局部的都设置到此次调用过程准备材料中。获取所需InitBinder与ModelAttribute代码过程十分类型(先获取局部再全局最后一起缓存),获取所需InitBinder如下
Class<?> handlerType = handlerMethod.getBeanType();
Set<Method> methods = this.initBinderCache.get(handlerType);
if (methods == null) {
methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
this.initBinderCache.put(handlerType, methods);
}
List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
// Global methods first
this.initBinderAdviceCache.forEach((controllerAdviceBean, methodSet) -> {
if (controllerAdviceBean.isApplicableToBeanType(handlerType)) {
Object bean = controllerAdviceBean.resolveBean();
for (Method method : methodSet) {
initBinderMethods.add(createInitBinderMethod(bean, method));
}
}
});
for (Method method : methods) {
Object bean = handlerMethod.getBean();
initBinderMethods.add(createInitBinderMethod(bean, method));
}
return createDataBinderFactory(initBinderMethods);
先是在局部initBinderCache获取,如果没有再去selectMethods,找到局部initBinder;之后再获取全局。之后全局与局部一起封装到initBinderMethods返回
6.2.2 调用
//获取值reslover
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
- 重要的返回值处理器介绍
我们标注@ResponseBody就会返回json,那么这里就是returnValueHanlders的作用,这些handler都实现了supportsReturnType和handleReturnType两个方法
其中处理@ResponseBody的returnValueHanlders 为RequestResponseBodyMethodProcessor类,其处理流程如下
7. 应用默认视图名
applyDefaultViewName(processedRequest, mv);
/**
* Do we need view name translation?
*/
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
8. interceptor的postHandler执行
mappedHandler.applyPostHandle(processedRequest, response, mv);
9. 异常处理
写在processHandlerException内部,如果再前面的步骤发生异常,catch到,那么在这里就会检测到,来进行处理
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
异常处理并不复杂,复杂的是异常处理也是个方法,也要涉及参数和返回值的处理,与之前adapter执行handler方法十分类似,如下图
10. 页面渲染
这里我们要十分主要的是,会根据我们是否有mv,如果有mv就会进入视图渲染,否则返回原始数据。
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
这里也很好理解,那如何会返回原始数据呢,我就拿@ResponseBody的returnValueHanlders 为RequestResponseBodyMethodProcessor类说下
再进行处理时进入的入口有很关键的一条语句,这里表明不需要mv
mavContainer.setRequestHandled(true);
之后再adapter的handle的最后一个getModelAndView方法执行如下:如果mavContainer.isRequestHandled() 为ture ,那么返回null,返回null就会导致mv为空,这样不会经过渲染而直接返回视图
modelFactory.updateModel(webReqest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
ModelMap model = mavContainer.getModel();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
11. 执行interceptor的triggerAfterCompletion
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}