SpringMVC DispatcherServlet 源码解析

371 阅读6分钟

开始第一篇框架源码解析: DispatcherServlet.doDispatcher(HttpServletRequest request, HttpServletResponse)方法解析:

首先了解SpringMVC的9大组件:

如果需要自己配置相关组件,注意事项:单个对象的id民称必须保持和对象名一致,否则加载不了该组件.比如:multipartResolver组件只会扫描id = “multipartResolver”的bean,然后赋值.

如果没有配置这些组件,SpringMVC也给了默认配置,除了multipartResolver = null; 这些配置放在了DispatcherServlet.properties文件内.通过initStrategies(ApplicationContext context)方法来初始化.

1、当客户端发出一个请求时,请求先经过配置的filters过滤,再被dispatcherServlet类接收.

2、disparcherServlet接收请求后,不管是get、post、delete或者是put方法,都是调用FrameworkServlet.processRequest(HttpServletRequest request, HttpServletResponse response)方法来处理.在该方法中对LocalContext、RequestAttributes做了缓存处理,缓存在当前线程的ThreadLocal中,在最后的finally方法内会reset掉这些缓存!实际通过调用doService(HttpServletRequest request, HttpServletResponse response)方法来进行对request的处理.

然后在doService方法内将localResolver、themeReslover等组件放到request的attributes里面,紧接着就调用doDispatcher(HttpServeletRequest request, HttpServletResponse resposne)方法处理请求.

3、在doDispatcher方法中分为以下几个步骤处理请求:

3.1 调用getHandler(request)方法得到包装了HandlerMethod和拦截器的HandlerExecutionChain类对象: 先遍历handlerMappings组件,通过handlerMapping.gethandler(request)方法,检查返回值是否为null确认是否在该handlerMapping内存在这样的映射.

底层用request的路径查询是否存在与路径对应的handler,找到即返回一个HandlerMethod对象,若没有,则查看下一个handlerMapping对象,直至遍历完成.若仍未找到则抛出异常.我用的是RequestMapping注解,所以能在RequestMappingHandlerMapping内的targetMap内找到对应的HandlerMethod处理器类,里面封装了Handler类信息和对应路径的method信息.在得到handlerMethod之后,还要收集所有的拦截器类作为一个list,整合handlerMethod和list生成并返回handlerExecutionChain对象,至此getHandler流程执行完毕.

3.2在得到mappedHandler()之后就要获取handler的适配器类,通过调用gethandlerAdapter(HandlerMethod handler)方法获得HandlerAdapter:

同样的,这边也是遍历所有的HandlerAdapters,通过调用handlerAdapter.support(handler)方法来确认该handlerAdapter是否支持此handler.若支持,则返回该handlerAdapter.

RequestMappinghandlerAdapter类内的实现是通过判断handler instance of HandlerMethod来确认是否支持该handler.实际上,RequestMappingHandlerAdapter.support(request)方法还提供了一个扩展点,一个直接return true的判断方法.

3.3在得到handlerAdapter之后就可以执行目标方法了,但是在执行目标方法之前,要先执行拦截器的prehandle()方法,具体是用之前得到的mappedHandler.applyPreHandle(request, response),遍历拦截器的list,依次执行拦截器的prehandle方法.

3.4然后就调用handlerAdapter.handler(request, response, handler)方法来执行目标方法,返回ModelAndView类的实例: RequestMappinghandlerAdapter首先装配了一个ServletInvocableHandlerMethod,里面包装了handlerMethod、argumentResolvers、returnValueHandlers、DataBinderFactory、parameterNameDiscoverer.

同时也创建了一个ModelAndViewContainer用来封装返回结果,对象初始化的时候会默认创建一个BindingAwareModelMap类型的defaultModel的属性,当处理器方法的参数列表内包含Map、Model等类型的时候,就会将这个defaultMap的引用赋值给参数,当需要传递数据时,只需在这些Map、Model内add,即可传入mavContainer内.

再装配好这两个类之后,就开始执行servletInvocableHandlerMethod.invokeAndHandle(request, mavContainer)方法,即利用反射技术调用方法,然后将结果保存在mvaContainer中.

在用反射之前,还需要给方法的参数列表赋值,所以首先从HandlerMethod里面取得类型为MethodParameter数组的参数列表,里面封装了每个参数的类型,index等信息.然后对这个数组循环遍历,对于每一个MethodParameter,都会在argumentResolers里面查找是否有匹配的解析器,通过不同的resolver.supportParameter(parameter)方法实现来确定,如果有对应的resolver,则将这对parameter 和resolver放到argumentResolverCache缓存里面,方便下次调用.

在得到resolver之后就可以开始对parameter解析并绑定request里面的值. 在AbstractNameValueMethodResolver类里面实现是靠先得到参数的参数名,然后用request.getParameter(name)来查找值.

参数名匹配和使用@RequestParam注解就是RequestParamMethodArgumentResolver解析器,继承了AbstractNameValueMethodResolver,采用命名匹配.

而Map类型使用MapMethodProcessor解析器来解析参数,直接返回了mavContainer里面的defaultModel,即前文提到的默认Model.

而Model类型使用ModelMethodProcessor解析器来解析参数,它也是直接返回了mavContainer内的dafaultModel对象引用.

至此,参数解析完成,就可以用反射InvocableHandlerMethod.getBridageMethod().invoke(bean, args)调用目标方法.得到返回值returnValue,这个时候returnValueHandlers开始工作,调用returnValueHandlers.handlerReturnValue(returnValue, returnValueType, mavContainer, request)方法对返回值进行处理.在这个方法里面,也会遍历所有的returnValueHandler来找到哪个handler可以supportReturnType(returnValueType),返回这个returnValueHandler.

直接用字符串表示的返回会被HandlerMethodReturnValueHandlerComposite处理器处理.

然后就用这个returnValuehandler的handleReturnValue(returnValue, returnValueType, mavContainer, request)方法来处理返回值.当前配置下会将返回的path放入macContainer的viewName属性下.

这个时候就可以创建一个ModelAndView对象来返回执行结果,调用getModelAndView(mavContainer, modelFactory, request)来创建.

3.5上面的步骤返回ModelAndView对象后,先进行拦截器的posthanlde方法,这个是倒序,和proHandle方法顺序相反.

3.6接下来就是视图渲染环节:

通过调用processDispatchResult(request, response, mappedHandler, mv, dispatcherException)方法来实现,这个方法的核心方法是调用render(mv, request, response)方法来渲染视图.

而在render方法内,它resolverViewName(viewName, model, locale, request)方法来创建一个View对象,这个创建过程是通过遍历所有的ViewResolvers来确定哪个viewResolver可以通过resolverViewName(viewName, locale)方法创建出View对象.

我这边只配置了一个InternalResourceViewResolver,这个ViewResolver创建View的过程为:

先判断ViewName是不是以“redirect:”开头,如果是,则截掉“redirect:”字符串后形成的新串作为url,再封装到RedirectView对象里面返回.

如果不是,则判断是不是以“forward:”开头,如果是,则截掉“forward:”字符串后形成的新串作为url,再封装到InternalResourceView对象里面返回.

如果都不是,就会创建一个InternalResourceView,然后通过拼串生成新的url(prefix + viewName + suffix), 设置到View里面返回.

生成View对象后,就可以通过View.render(model, request, response)方法先将Model数据存放到一个mergedModel对象内,同时放入的还有staticAttributes、pathVars、requestContextAttribute等数据.然后通过e xexposeModelAsRequestAttributes(model,request)方法将数据逐一放入request域,最后通过RequestDispatcher.forward(request, response)方法跳转页面.

3.7最后执行拦截器的afterCompletion()方法.