Solo  当前访客:1 开始使用

Nick1407 的个人博客

公众号:JavaCase

请求的处理机制


一、请求流程图
image.png

二、流程示意

servlet处理方法都是通过HttpServlet的service方法开始,FrameworkServlet重写了父类HttpServlet的service方法。根据类型调用不同的do方法,如doGet,doPost。这些方法在FrameworkServlet内被重写了,所以最终调用的还是FrameworkServlet内的相应方法。它是Spring MVC框架中的基本Servlet实现类,通过JavaBean的方式集成了Application context。
FrameworkServlet提供两个功能:对每一个请求,无论是否成功处理,都会在每个请求上发布事件。为每个servlet管理一个WebApplicationContext实例,每个servlet都有自己的配置空间,相互独立。

image.png

各请求方法都会执行processRequest方法,查看processRequest(request, response)的实现,具体可以分为几个步骤: 获取上一个请求的参数、重新建立新的参数、设置到ContextHolder、doservice()处理请求、恢复request、发布事件。在initContextHolders方法中Spring替换了localthread的内容,threadLocal只针对当前请求,请求结束时要恢复。

image.png

类DispatcherServlet的doService()处理请求。这是一个前端控制器,集中提供请求处理机制,返回ModelAndView,然后通过视图解析器解析,渲染,最后返回给请求者。doService()判断该请求如果是包含请求,也就是说,不是来自外部的顶级HTTP请求。然后会将request对象的参数进行备份,以便后续恢复这些属性。接着将全局对象注入到request对象中,让handler和view对象可用。接着调用doDispatch()方法

image.png
image.png
image.png

在doDispatch()方法中,checkMultipart(request);判断这个请求是否是Multipart,如果是Multipart请求就交给multipartResolver处理,如果不是Multipart返回当前的请求。
image.png

接下来会执行getHandler方法,在getHandler(processedRequest)遍历所有的handlerMapping,调用其中的getHandler(request)方法获得HandlerExecutionChain对象,处理器执行链由处理器对象和拦截器组成。

image.png

image.png

在getHandler方法中,通过getHandlerInternal(request)获得handler。

image.png

headler的获取方式,查看getHandlerInternal(request)方法,先获取查询路径查询路径lookupPath然后获得,
mappingRegistry的读锁。mappingRegistry维护了所有mapping到具体业务处理的handlerMethod的映射关系,根据映射关系获取相应的目标。

image.png

然后执行lookupHandlerMethod(lookupPath, request)获取hanlderMethod,该方法查找当前请求的最佳匹配处理程序方法,如果找到多个匹配项,则选择最佳匹配项。

image.png

如果获得的handlerMethod不为空,调用createWithResolvedBean()方法,返回HandlerMethod对象,在createWithResolvedBean的方法中如果当前handlerMethod中的bean只是名称,而不是实例时,那么就会通过名称获得bean的实例,最后释放mappingRegistry的读锁。

image.png
image.png

接着通过getHandler(HttpServletRequest request)调用getHandlerExecutionChain(handler, request),遍历所有的handlerInterceptor,把handler和handlerInterceptor封装成handlerExecutionChain。MappedInterceptor里包含includePatterns和excludePatterns属性,分别设置需要被拦截的url和不需要被拦截的url。
image.png
image.png

然后通过doDispatch调用方法getHandlerAdapter(mappedHandler.getHandler())获取当前handlerMethod的适配器,循环当前所有的handlerAdapters,通过supports()判断是否支持当前handlerMethod

image.png

image.png

判断request的请求方式,如果是GET或者是HEAD,会利用HTTP缓存,提高应用程序的性能。获取用户当前请求上一次请求的时间戳,通过checkNotModified()判断是否修改过,将'If-Modified-Since'或'If-Unmodified-Since'请求头与lastModified相比教。如果没有修改过,返回状态码304,客户端满足缓存条件。
image.png
image.png

然后执行handlerExecutionChain中的拦截器preHandle方法,该方法遍历HandlerExecutionChain中的所有拦截器,SpringMVC中的Interceptor拦截器是链式的,可以同时存在多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而Interceptor中的preHandle方法都会在 Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构可以进行中断,这种中断方式是令preHandle的返回值为false,当preHandle的返回值为false的时候整个请求就结束了,然后调用triggerAfterCompletion(request, response, null)进行资源清理。通过记录interceptorIndex来标志当前执行的拦截器。
image.png

继续执行发现handlerAdapter开始调用handle方法,返回ModelAndView
进入到handleInternal会发现
该方法中首先调用WebContentGenerator中的checkRequest()方法,判断支持的请求方式中是否包含当前请求的方式,如果supportedMethods不为空且不支持当前请求方式,会抛出 HttpRequestMetohdNotSupportedException。如果需要session且从当前请求获得不到session,同样抛出HttpSessionRequiredException异常。
image.png
image.png
image.png

接着通过synchronizeOnSession标识符,判断调用invokeHandlerMethod是否需要同步机制。 然后调用invokeHandlerMethod(),该方法首先创建WebDataBinderFactory实例,用于创建WebDataBinder对象,用于web参数绑定。接着创建出ModelFactory实例,ModelFactory作用是在控制器方法调用前初始化Model模型,调用后对Model模型进行更新。在初始化时,通过调用被@ModelAttribute注解的方法,Model模型会在会话中被临时存储的属性填充。

image.png
image.png

WebDataBinderFactory创建过程:
获得handlerMethod所在类的类型。通过所在类的类型获得从initBinderCache缓存中获得当前类所有的方法。这些方法应该是被@InitBinder注解的方法。如果methods等于空,那么我们去获得当前类下被@InitBinder注解的方法,并放入到initBinderCache缓存中。然后优先遍历被@ControllerAdvice注解全局类中的方法,再遍历被@Controller注解的类的方法。通过createInitBinderMethod(bean, method)方法创建InvocableHandlerMethod对象(用于参数准备,准备当中会用到WebDataBinderFactory创建WebDataBinder实例进行参数转换解析绑定,方法调用),并且放入到initBinderMethods集合中。最后调用createDataBinderFactory(initBinderMethods)方法创建ServletRequestDataBinderFactory实例
image.png
image.png

ModelFactory创建过程:
首先根据handlerMethod所在类的类型从sessionAttributesHandlerCache缓存获取sessionAttributesHandler对象,接着通过modelAttributeCache中获取handlerMethod所在类中所有被@ModelAttribute注解且没有被@RequestMapping注解的方法。 如果没有从缓存中查找到,那么通过
MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS)查找,并加入到modelAttributeCache缓存中。然后通过createModelAttributeMethod(binderFactory, bean, method)方法创建InvocableHandlerMethod对象,并放入到attrMethods集合中,最后通过handlerMethods集合、WebDataBinderFactory实例,SessionAttributesHandler实例创建出ModelFactory实例。

image.png
image.png

接下来对handlerMethod进一步包装。调用createInvocableHandlerMethod的作用是对程序的参数、返回值和ResponseStatus进行处理。
image.png

接着初始化容器ModelAndViewContainer,将request里的属性名为"INPUT_FLASH_MAP"的重定向参数注入到容器中的model模型中。FlashMap的作用是在redirect中传递参数。重定向是会生成新的request,那么传递参数就不能直接用request进行传递。
image.png
image.png
然后初始化模型,modelFactory调用initModel方法,在initModel方法中首先从request中获取检索@SessionAttribute中名称的属性,以Map<String, Object>的结构存储起来,并且放入到
ModelAndViewContainer容器的model中。
image.png
image.png

接着调用invokeModelAttributeMethods(request, container)方法,将被@ModelAttribute注解的handlerMethod中的模型填充到ModelAndViewContainer容器中的model。只有当容器中包含当前@ModelAtrribute中的属性时才添加该属性至容器。同时还要判断当前@ModelAttribute中的属性能不能添加到容器中,如果不能,那么放到容器中的bindingDisabledAttributes进行标记。然后提前调用被@ModelAttribute注解的handlerMethod,只有handlerMethod的返回值类型不是void,才能将数据绑定到容器中的model里。
image.png

通过initModel调用findSessionAttributeArguments(handlerMethod)方法,我们得到了合适的参数名称集合。遍历这个集合,我们要判断ModelAndViewContainer容器中是否存在相同名称的参数。如果不存在,我们从sessionAttributeStore根据名称中获得这个参数的值,最后将参数绑定到容器中。
image.png

然后设置ModelAndViewContainer容器使用defaultModel(默认模型),而不是redirectModel(重定向模型)。接着处理一些异步请求。
image.png

接下来invocableMethod去调用invokeAndHandle这个方法。其实是间接调用handlerMethod,然后处理handlerMethod的返回值。
image.png

invokeForRequest(webRequest, mavContainer, providedArgs)这个方法会返回handlerMethod的返回值。这个方法在给定请求的上下文中解析handlerMethod的方法参数后,然后去调用handlerMethod。

image.png

调用doInvoke(args)方法,我们可以看到通过ReflectionUtils.makeAccessible(getBridgedMethod())让handlerMethod方法具有访问性,必要要显式的设置它具有访问性。然后准备好handlerMethod所在类的实例和方法参数,反射调用handlerMethod。
image.png

获得方法返回值后接着是设置ResponseStatus的状态
image.png
image.png

实际上handlerMethod的返回值处理是通过HandlerMethodReturnValueHandlerComposite的handleReturnValue()方法,遍历所有的返回值处理器,通过supportsReturnType()判断是否支持该返回值的类型。如果类型支持的话,那么就让合适的HandlerMethodReturnValueHandler去处理handlerMethod的返回值。

image.png
image.png
image.png

****-
接着在invokeHandlerMethod调用getModelAndView方法,方法中首先调用modelFactory.updateModel(webRequest, mavContainer)将被@SessionAtrributes注解的模型属性上升到会话级别。如果mavContainer采用的是response直接处理策略,说明没有采用view的解决方案,直接返回null即可。将ModelAndViewContainer中的model、视图的名称、HttpStatus填充到ModelAndView中。如果mavContainer没有指定逻辑视图(或者说 view不是String类型的)的话,那么就设置视图对象。如果model是RedirectAttributes的实例,那么说明是model是重定向所需要的属性,我们把model填充到FlashMap即可。

image.png

回到RequestMappingHandlerAdapter中的hanlderInternal()方法中,如果response的响应条没有设置Cache-control属性的话,如果handlerMethod对应的SessionAttributesHandler中维护了被@SessionAtrribute注解的model,那么设置Cache-control为no store模式。否则设置Cache-control为-1。

image.png
image.png

回到DispatcherServlet中的doDispatch()方法,我们通过适配器调用HandlerExecutionChain中的handler返回ModelAndView,如果ModelAndView中没有视图引用,那么申请设置默认的视图名称。然后调用HandlerExecutionChain中所有的拦截器中的postHandle()方法,对handlerMethod返回的结果进行加强处理。

image.png
image.png

接着调用processDispatchResult开始对处理程序调用返回的结果进行处理,正常返回ModelAndView对象,如果出现异常该则将相应的错误页面设置到View,并在其中的processHandlerException方法中用到了HandlerExceptionResolver。processHandlerException()内部实现是遍历DispatcherServlet中的handlerExceptionResolvers(程序异常解析器集合),如果当前有异常解析器能够处理这个异常且处理完毕后返回的ModelAndView不为空,那么跳出循环。
image.png

回到DispatcherServlet中的processDispatchResult(),如果ModelAndView实例不为空且modelAndView中的model和view属性不为空,那么进行render()操作,它实际上是为了呈现ModelAndView,这是处理请求的最后一步,里面涉及到将逻辑视图转换成真正的物理视图。render()操作之后,判断errorView是否为true,如果为true,代表已经有错误视图去响应错误,那么就可以清理request中一些关于错误的属性(status_code、exception_type、message、exception、request_uri、servlet_name)。
image.png
image.png
image.png

render方法内resolveViewName()其实将视图名称转换成真正的视图对象。通过遍历当前所有的viewResolvers,如果视图解析器解析后的view对象不为空的话,那么直接返回当前view对象。
image.png
获取到合适的view对象,那么调用其本身的render()方法,将model对象填充到到view对象中,完成渲染操作。
image.png

参考链接:https://www.jianshu.com/p/6f841d81ed72

0 0