SpringMVC与Servlet的关系
1)SpringMVC是基于Servlet实现的,启动入口也是起于Servlet的init方法
2)Servlet.init初始化方法由外部接口第一次访问Servlet时触发
3)Servlet.init方法最终会调FrameworkServlet.initWebApplicationContext方法
4)该方法会创建XMLWeb容器(Controller),并把Web容器(Service和Dao)设置父容器
5)完成FrameworkServlet的XMLWeb容器创建和刷新后,就会初始化外层DispatcherServlet的基本组件
6)DispatcherServlet的九大组件完成初始化后,DispatcherServlet就可统一处理所有外部请求
父容器Web容器(Service和Dao)的创建时机
1)前面忽略了一个问题,父容器Web容器(Service和Dao)的创建和刷新时间
2)该容器是在ContextLoaderListener接口创建和刷新的,ContextLoaderListener实现了ServletContextListener接口
3)实现了ServletContextListener接口可监听ServletContext的生命周期,包括ServletContext的创建和销毁
4)所以,tomcat启动创建ServletContext时,会触发监听器的ServletContextListener.contextInitialized方法
5)contextInitialized方法内部会创建和刷新父容器Web容器(Service和Dao),并把容器存到ServletContext中
6)当XMLWeb容器(Controller)需要父容器Web容器(Service和Dao),就可从ServletContext获取
DispatcherServlet介绍
1)DispatcherServlet是一个Servlet,拦截所有请求并设计出一套自己的映射处理逻辑
2)DispatcherServlet设计思路:DispatcherServlet下的Controller替换了tomcat下的Servlet
3)出现这种设计思路的内因,是原生Servlet在使用上不友好(参数处理,映射规则配置等不够简单)
DispatcherServlet的九大组件
1)HandlerMapping:URL和Controller的映射规则
2)HandlerAdapter:不同实现的Controller,调用方式也不同,HandlerAdapter为了兼容Controller
3)HandlerExceptionResolver:处理Controller发生的异常
4)ViewResolver:定位到具体页面,并把页面的参数替换
5)RequestToViewNameTranslator:Handler没有设置View,可从RequestToViewNameTranslator获取ViewName
6)LocaleResolver:解析视图需要Locale,LocaleResolver定位Locale,暂不关注
7)ThemeResolver:用于解析主题,暂不关注
8)MultipartResolver:处理上传文件请求,把普通request包装成MultipartHttpServletRequest
9)FlashMapManager:管理FlashMap,FlashMap可用在redirect中传递参数,暂不关注
DispatcherServlet处理请求流程
1.servlet针对所有外部访问请求都会调用到DispatcherServlet.doDispatch(request,response)方法处理
2.doDispatch(request,response)方法主要执行逻辑
1)根据request中的访问路径定位到mappedHandler对象
2)mappedHandler对象保存了Handler(Controller下method方法反射对象)和拦截器
3)根据Handler(Controller)的不同类型,定位到具体的HandlerAdapter(封装了调用Handler的逻辑)
4)按序执行所有拦截器的preHandle方法
5)执行HandlerAdapter.handle方法,方法内部真正执行Handler(Controller下method方法反射对象)逻辑
6)按序执行所有拦截器的postHandle方法
7)处理HandlerAdapter.handle方法返回的ModelAndView对象,渲染并返回结果
8)按序执行所有拦截器的afterCompletion方法
定位mappedHandler(HandlerExecutionChain)的逻辑
1.DispatcherServlet内部保存了九大组件之一的handlerMappings
2.handlerMappings是一个List,保存了不同的handlerMappings,用的最多的是RequestMappingHandlerMapping
3.RequestMappingHandlerMapping可获得@RequestMapping注解的Controller方法(也称Handler)
4.具体定位逻辑:通过遍历handlerMappings,然后调用HandlerMapping.getHandler(request)方法
5.通过传入的request参数(封装有url),定位到具体的HandlerExecutionChain就直接返回,否则继续遍历
6.HandlerExecutionChain包装拦截器和Handler,然后把HandlerExecutionChain返回给上层
7.上层得到HandlerExecutionChain,就可调用拦截器和Handler的具体方法
定位HandlerAdapter的逻辑
1.DispatcherServlet内部保存了九大组件之一的handlerAdapters
2.handlerMappings是一个List,保存了不同的HandlerAdapter,用的最多的是RequestMappingHandlerAdapter
3.RequestMappingHandlerAdapter处理加了@RequestMapping注解的Controller方法(也称Handler)
4.具体定位逻辑:通过遍历handlerAdapters,调用HandlerAdapter.supports(Handler)方法
5.supports(Handler)方法根据Handler的类型,来判断是否支持
6.若支持,则直接返回HandlerAdapter,若不支持继续遍历
HandlerAdapter的必要性
初学springMVC都有这个疑惑,为什么有HandlerAdapter这个对象?
其实在认知中,Handler就是一个反射方法对象(method),method.invoke(o,args)就可直接调用,但是springMVC在设计之初选择增加了HandlerAdapter对象,这里我们就思考一下内因,这是因为调用该方法前后,我们要考虑两个问题,参数处理和结果封装。参数部分我们要考虑如何把request的参数解析成args,特别是springMVC设计出了不同的参数注解(reuqestParam,requestBody等),我们如何优雅的支持这些好用的注解并从reques取出封装到args中;返回结果部分我们要考虑组装一个可展示的页面,页面包括html静态元素和数据动态元素,在springMVC中就是用ModelAndView来描述。通过以上描述,参数处理和结果集组装是很复杂的,逻辑也很繁琐,如果这些逻辑放到外部实现将会很不优雅,所以springMVC提出了一个HandlerAdapter概念,专门处理这些繁琐的逻辑。
常用的RequestMappingHandlerAdapter处理流程
1.调用HandlerAdapter.handle方法,传入三个参数request,response,Handler对象
2.创建ServletInvocableHandlerMethod可执行对象,该对象包装了Handler对象并准备了必要的执行上下文
3.执行上下文包括所有的参数处理器argumentResolvers和结果处理器returnValueHandlers
4.调用ServletInvocableHandlerMethod.invokeAndHandle方法处理请求
5.首先方法内部会获取Handler(Controller的方法)方法的待解析参数(数组parameters-MethodParameter[])
1)parameters参数是Handle方法调用时必传的参数,目前处于待解析的状态,还没从request获取值
2)MethodParameter对象包装了参数名称和注解信息(例如RequestParam)
3)此时方法内部持有MethodParameter对象和request对象,从request解析出对应参数顺理成章
6.遍历parameters,针对每一个MethodParameter封装的注解信息,选择对应的参数解析器来解析request内部值
7.例如标志了@requestParam的参数,会使用RequestParamMethodArgumentResolver解析
1)若遍历出来的MethodParameter加了@requestParam注解,则用RequestParamMethodArgumentResolver解析
2)然后调用解析器的resolveArgument(MethodParameter,request),根据特定规则从request取值并返回
8.遍历parameters(MethodParameter[]),对于获取到的每个参数值都会赋值到一个新的数组args
9.最后得到解析出来的args参数,就可调用Handle.invoke(Controller,args)
10.剩下的逻辑就是处理ModelAndView的返回结果