0


SpringMVC源码解析(二):请求执行流程

SpringMVC源码系列文章

SpringMVC源码解析(一):web容器启动流程

SpringMVC源码解析(二):请求执行流程


目录

前言

**前文中我们介绍了SpringMVC容器的启动,包括前端控制器DispatcherServlet对象的创建,过滤器添加到Tomcat容器的过滤器集合中,将所有拦截器、跨域配置、消息转换器等配置统一添加到各自集合中,解析@RequestMapping注解生成请求路径和Controller方法的映射map。本章来研究下

请求的执行过程

。**

**说到请求过程,那么得先说下入口在哪里?入口肯定是统一分发请求给处理程序的

DispatcherServlet

,DispatcherServlet归根结底也是Servlet。Tomcat通过请求Mapping映射和Servelt对应关系找到Servelt,调用Servelt之前会执行过滤器链,所有过滤器放行才会走到Servelt真正的执行逻辑。**

  • 依照常见的post请求为例
// 接受User对象并返回@PostMapping("/test")@ResponseBodypublicUsertest(@RequestBodyUser user){
    user.setName("李四");System.out.println(user);return user;}
  • 方法栈调用链

在这里插入图片描述

  • HttpServelt#service分水岭doPost方法,只要找到DispatcherServelt重写的doPost方法就是入口了

在这里插入图片描述

本文就不讲过滤器的调用了,因为从DispatcherServelt开始,过滤器链已经执行完成,之前文章Tomcat源码解析(八):一个请求的执行流程(附Tomcat整体总结)有介绍过。

DispatcherServlet入口

DispatcherServlet的类图如下:

在这里插入图片描述

从doPost到doDispatch方法

  • doPost方法是由DispatcherServelt的父类FrameworkServlet实现的
  • 不论post还是get请求都是调用同一个方法processRequest(request, response)
  • 对于方法参数request和respons都是Tomcat容器创建的,以前文章Tomcat源码解析(七):底层如何获取请求url、请求头、json数据?有具体介绍

在这里插入图片描述

  • 主要方法doService(request, response)
// FrameworkServlet类方法protectedfinalvoidprocessRequest(HttpServletRequest request,HttpServletResponse response)throwsServletException,IOException{long startTime =System.currentTimeMillis();Throwable failureCause =null;...try{doService(request, response);}catch(ServletException|IOException ex){
        failureCause = ex;throw ex;}catch(Throwable ex){
        failureCause = ex;thrownewNestedServletException("Request processing failed", ex);}finally{...}}
  • 主要方法doDispatch(request, response)
// DispatcherServlet类方法@OverrideprotectedvoiddoService(HttpServletRequest request,HttpServletResponse response)throwsException{// ... 设置一堆Attribute属性try{doDispatch(request, response);}finally{...}}

**核心方法

doDispatch

**

  • 这个方法包含了SpringMVC的整个执行流程
protectedvoiddoDispatch(HttpServletRequest request,HttpServletResponse response)throwsException{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);// 如果是上传请求,这个参数置为true,最后会去清理资源
            multipartRequestParsed =(processedRequest != request);// 获取HandlerExcutionChain (内部包括Handler)
            mappedHandler =getHandler(processedRequest);if(mappedHandler ==null){// 请求url找不到404就会走到这里noHandlerFound(processedRequest, response);return;}// 获取适配器HandlerAdapter ha =getHandlerAdapter(mappedHandler.getHandler());// get请求缓存相关,以后有机会单独将String method = request.getMethod();boolean isGet =HttpMethod.GET.matches(method);if(isGet ||HttpMethod.HEAD.matches(method)){long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if(newServletWebRequest(request, response).checkNotModified(lastModified)&& isGet){return;}}// 调用拦截器的前置方法preHandleif(!mappedHandler.applyPreHandle(processedRequest, response)){return;}// 执行handler方法// 需要跳转页面这里才会返回ModelAndView对象,否则@ResponseBody返回对象这里返回null
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if(asyncManager.isConcurrentHandlingStarted()){return;}applyDefaultViewName(processedRequest, mv);// 调用拦截器的后置方法postHandle
            mappedHandler.applyPostHandle(processedRequest, response, mv);}catch(Exception ex){
            dispatchException = ex;}catch(Throwable err){//从4.3开始,我们也在处理处理程序方法抛出的错误,//使它们可用于@ExceptionHandler方法和其他场景。
            dispatchException =newNestedServletException("Handler dispatch failed", err);}// 处理结果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch(Exception ex){// 调用拦截器请求处理完成后的回调triggerAfterCompletiontriggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch(Throwable err){// 调用拦截器请求处理完成后的回调triggerAfterCompletiontriggerAfterCompletion(processedRequest, response, mappedHandler,newNestedServletException("Handler processing failed", err));}finally{if(asyncManager.isConcurrentHandlingStarted()){// 异步处理的回调if(mappedHandler !=null){
                mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else{// 如果是上传请求,清理相关资源if(multipartRequestParsed){cleanupMultipart(processedRequest);}}}}

一、获取HandlerExcutionChain(包括Handler)

**遍历所有的HandlerMapping,只要getHandler方法能获取到

HandlerExecutionChain

立即返回。**

// DispatcherServlet类方法@NullableprotectedHandlerExecutionChaingetHandler(HttpServletRequest request)throwsException{if(this.handlerMappings !=null){for(HandlerMapping mapping :this.handlerMappings){HandlerExecutionChain handler = mapping.getHandler(request);if(handler !=null){return handler;}}}returnnull;}

**如下这三个HandlerMapping是web容器启动时候加载的,上篇文章SpringMVC源码解析(一):web容器启动流程有具体介绍。三个轮流调用getHandler方法,而HandlerMapping也有顺序的,

RequestMappingHanderMapping

排序为0优先级最高,而它也是处理

@RequestMapping

注解的映射关系的映射器。**

在这里插入图片描述

**调用

抽象类

的方法,那么上面看到的三个HandlerMapping应该都会调用此方法,而这里肯定有一些核心的不同的方法实现在不同的HandlerMapping具体实现类中,典型的

模板方法

设计模式。**

// AbstractHandlerMapping类方法@Override@NullablepublicfinalHandlerExecutionChaingetHandler(HttpServletRequest request)throwsException{// 获取HandlerObject handler =getHandlerInternal(request);if(handler ==null){
        handler =getDefaultHandler();}if(handler ==null){returnnull;}// handler为bean的名称// 这种Controller应该是实现Controler、HttpRequestHandler接口的方式// 以前的老实现方式,暂不研究if(handler instanceofString){String handlerName =(String) handler;
        handler =obtainApplicationContext().getBean(handlerName);}...// 获取执行链(包括Handler和拦截器)HandlerExecutionChain executionChain =getHandlerExecutionChain(handler, request);// ...打印日志// 添加跨域设置(本身也是拦截器)到拦截器链中第一个位置if(hasCorsConfigurationSource(handler)||CorsUtils.isPreFlightRequest(request)){CorsConfiguration config =getCorsConfiguration(handler, request);if(getCorsConfigurationSource()!=null){CorsConfiguration globalConfig =getCorsConfigurationSource().getCorsConfiguration(request);
            config =(globalConfig !=null? globalConfig.combine(config): config);}if(config !=null){
            config.validateAllowCredentials();}
        executionChain =getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;}

1、获取Handler

  • 主要内容就是调用父类AbstractHandlerMethodMapping的相同方法
// RequestMappingInfoHandlerMapping类方法@Override@NullableprotectedHandlerMethodgetHandlerInternal(HttpServletRequest request)throwsException{
    request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);try{// 核心方法调用父类的getHandlerInternal方法returnsuper.getHandlerInternal(request);}finally{ProducesRequestCondition.clearMediaTypesAttribute(request);}}
  • 通过request获取查找路径
  • 通过查找路径获取HandlerMethod
// AbstractHandlerMethodMapping类方法@Override@NullableprotectedHandlerMethodgetHandlerInternal(HttpServletRequest request)throwsException{// 通过request获取查找路径String lookupPath =initLookupPath(request);this.mappingRegistry.acquireReadLock();try{// 通过查找路径获取HandlerMethodHandlerMethod handlerMethod =lookupHandlerMethod(lookupPath, request);return(handlerMethod !=null? handlerMethod.createWithResolvedBean():null);}finally{this.mappingRegistry.releaseReadLock();}}

1.1、通过request获取查找路径

  • 截取请求uri,如图/springmvc/test,/springmvc为项目路径,/test为我们需要的查找路径

在这里插入图片描述

1.2、通过查找路径获取HandlerMethod

**这个方法的核心内容就是从之前讲的SpringMVC源码解析(一):web容器启动流程注册的

两个map

获取数据。**

在这里插入图片描述

// AbstractHandlerMethodMapping类方法@NullableprotectedHandlerMethodlookupHandlerMethod(String lookupPath,HttpServletRequest request)throwsException{List<Match> matches =newArrayList<>();// 通过查找路径获取RequestMappingInfoList<T> directPathMatches =this.mappingRegistry.getMappingsByDirectPath(lookupPath);if(directPathMatches !=null){// 通过RequestMappingInfo获取HandlerMethodaddMatchingMappings(directPathMatches, matches, request);}...if(!matches.isEmpty()){Match bestMatch = matches.get(0);if(matches.size()>1){//...匹配多个,抛错异常}
        request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());handleMatch(bestMatch.mapping, lookupPath, request);// 获取HandlerMethod并返回return bestMatch.getHandlerMethod();}else{returnhandleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);}}

在这里插入图片描述

2、获取执行链(包括Handler、拦截器)

**我们自定义的拦截器统一用

MappedInterceptor

这个拦截器包装了一层,为了统一调用

matcher

方法,匹配此拦截器请求是否拦截本次请求,如果是则会添加到拦截器链中。**

// AbstractHandlerMapping类方法protectedHandlerExecutionChaingetHandlerExecutionChain(Object handler,HttpServletRequest request){// 创建HandlerExecutionChain对象HandlerExecutionChain chain =(handler instanceofHandlerExecutionChain?(HandlerExecutionChain) handler :newHandlerExecutionChain(handler));// 遍历所有的拦截器,这拦截器是web容器启动时候解析加载的的for(HandlerInterceptor interceptor :this.adaptedInterceptors){// 我们自定义的拦截器统一用MappedInterceptor这个拦截器包装了一层// 为了统一的匹配方法,下面调用machesif(interceptor instanceofMappedInterceptor){MappedInterceptor mappedInterceptor =(MappedInterceptor) interceptor;// matcher匹配当前请求路径是否符合拦截器的拦截请求if(mappedInterceptor.matches(request)){
                chain.addInterceptor(mappedInterceptor.getInterceptor());}}else{
            chain.addInterceptor(interceptor);}}return chain;}// 执行器链对象,主要就是两个属性handler:Handler对象,interceptorList:拦截器集合publicclassHandlerExecutionChain{privatefinalObject handler;privatefinalList<HandlerInterceptor> interceptorList =newArrayList<>();// 构造方法publicHandlerExecutionChain(Object handler){this(handler,(HandlerInterceptor[])null);}...}
  • 拦截器链最终的结果

在这里插入图片描述

二、获取适配器

看下HandlerAdapter接口

publicinterfaceHandlerAdapter{/**
     * 因为有多个HandlerMapping和HandlerAdapter
     * 对于HandlerAdapter是否支持对应的HandlerMapping,通过此方法判断
     */booleansupports(Object handler);/**
     * 具体调用Hangder的方法
     */@NullableModelAndViewhandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException;}

**因为不同的

Hander

(@RequestMapping、实现Controller接口、实现HttpRequestHandler接口)对应的

HandlerAdapter

(适配器)不一样,通过HandlerAdapter的

supports

方法判断当前HandlerAdapter

是否支持此次请求的Hander

。**

// DispatcherServlet类方法protectedHandlerAdaptergetHandlerAdapter(Object handler)throwsServletException{if(this.handlerAdapters !=null){for(HandlerAdapter adapter :this.handlerAdapters){if(adapter.supports(handler)){return adapter;}}}thrownewServletException("No adapter for handler ["+ handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");}
  • 这个是抽象类实现的supports方法,所有HandlerAdapter判断是否支持都会走这里
  • 其主要作用就是supportsInternal方法,在不同的HandlerAdapter实现类中重写- RequestMappingHandlerAdapter重写的supportsInternal返回true,表示其支持
// AbstractHandlerMethodAdapter类方法@Overridepublicfinalbooleansupports(Object handler){return(handler instanceofHandlerMethod&&supportsInternal((HandlerMethod) handler));}// RequestMappingHandlerAdapter类方法@OverrideprotectedbooleansupportsInternal(HandlerMethod handlerMethod){returntrue;}

**由上面HandlerAdapter接口可以猜到,

RequestMappingHandlerAdapter

适配器就是我们需要的,之后会通过handle方法去

执行Hangder方法

即调用

Controller#Method

。**

三、执行Handler(🔥重点)

**AbstractHandlerMethodAdapter类的handle方法即重写HandlerAdapter的handle方法,所有的HandlerAdapter执行Hangdler都会进入此方法,而具体的方法实现又要调用HandlerAdapter的实现类,如下,实现类就在

RequestMappingHandlerAdapter

类的

handleInternal

方法。**

// AbstractHandlerMethodAdapter类方法@Override@NullablepublicfinalModelAndViewhandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{returnhandleInternal(request, response,(HandlerMethod) handler);}
  • 执行Handle方法内又包括解析请求执行真正逻辑解析响应

在这里插入图片描述

  • 执行Handler并获取返回值,处理响应,如对象转化为json
// ServletInvocableHandlerMethod类方法publicvoidinvokeAndHandle(ServletWebRequest webRequest,ModelAndViewContainer mavContainer,Object... providedArgs)throwsException{// 执行Handler并获取返回值Object returnValue =invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);if(returnValue ==null){if(isRequestNotModified(webRequest)||getResponseStatus()!=null|| mavContainer.isRequestHandled()){disableContentCachingIfNecessary(webRequest);
            mavContainer.setRequestHandled(true);return;}}elseif(StringUtils.hasText(getResponseStatusReason())){
        mavContainer.setRequestHandled(true);return;}

    mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers !=null,"No return value handlers");try{// 处理响应,返回对象转换响应信息,如对象转化为jsonthis.returnValueHandlers.handleReturnValue(
                returnValue,getReturnValueType(returnValue), mavContainer, webRequest);}catch(Exception ex){// 异常向上抛throw ex;}}// InvocableHandlerMethod类方法,实现HandlerMethod接口@NullablepublicObjectinvokeForRequest(NativeWebRequest request,@NullableModelAndViewContainer mavContainer,Object... providedArgs)throwsException{// 获取请求参数Object[] args =getMethodArgumentValues(request, mavContainer, providedArgs);if(logger.isTraceEnabled()){
        logger.trace("Arguments: "+Arrays.toString(args));}// 执行真正逻辑returndoInvoke(args);}

1、获取请求参数

**拿到具体Controller的

Method

方法参数,遍历所有参数寻找支持每个参数类型的

参数解析器

,解析参数并返回。**

// InvocableHandlerMethod类方法,实现HandlerMethod接口protectedObject[]getMethodArgumentValues(NativeWebRequest request,@NullableModelAndViewContainer mavContainer,Object... providedArgs)throwsException{// 从HandlerMethod中获取参数信息// 之前项目启动就加载了Handler,里面包含了具体要执行的Controller的MethodMethodParameter[] parameters =getMethodParameters();if(ObjectUtils.isEmpty(parameters)){returnEMPTY_ARGS;}// 遍历所有的参数Object[] args =newObject[parameters.length];for(int i =0; i < parameters.length; i++){MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i]=findProvidedArgument(parameter, providedArgs);if(args[i]!=null){continue;}// 寻找支持当前参数类型的参数解析器if(!this.resolvers.supportsParameter(parameter)){thrownewIllegalStateException(formatArgumentError(parameter,"No suitable resolver"));}try{// 根据上一步获取的参数解析器解析参数并返回具体参数
            args[i]=this.resolvers.resolveArgument(parameter, mavContainer, request,this.dataBinderFactory);}catch(Exception ex){throw ex;}}return args;}

1.1、获取参数解析器

**遍历所有的

参数解析器

,调用参数解析器的

supportsParameter

方法,返回true,表示此解析器可以解析当前参数类型,而且将方法的参数与解析器放入缓存

argumentResolverCache

,以后同一个接口调用第二次,参数解析器直接从缓存中获取就可以,不再需要遍历调用supportsParameter方法去筛选获取。**

// HandlerMethodArgumentResolverComposite类方法@OverridepublicbooleansupportsParameter(MethodParameter parameter){returngetArgumentResolver(parameter)!=null;}@NullableprivateHandlerMethodArgumentResolvergetArgumentResolver(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;}
  • 参数解析器集合HandlerMethodArgumentResolver argumentResolvers中一共有27个- 几乎每个注解就是一个解析器,如@RequestParam@PathVariable等等
  • 参数解析器集合中下标7就是我们常用的@RequestBody注解参数解析器

在这里插入图片描述

  • supportsParameter方法简单明了,参数包含注解@RequestBody即可
// RequestResponseBodyMethodProcessor类方法@OverridepublicbooleansupportsParameter(MethodParameter parameter){return parameter.hasParameterAnnotation(RequestBody.class);}

1.2、解析@RequestBodey请求参数

// HandlerMethodArgumentResolverComposite类方法@Override@NullablepublicObjectresolveArgument(MethodParameter parameter,@NullableModelAndViewContainer mavContainer,NativeWebRequest webRequest,@NullableWebDataBinderFactory binderFactory)throwsException{// 获取参数解析器,此时上面已经筛查出来,放到argumentResolverCache缓存中HandlerMethodArgumentResolver resolver =getArgumentResolver(parameter);if(resolver ==null){thrownewIllegalArgumentException("Unsupported parameter type ["+
                parameter.getParameterType().getName()+"]. supportsParameter should be called first.");}// 解析方法return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}
  • 调用注解解析器RequestResponseBodyMethodProcessorresolveArgument方法- readWithMessageConverters:通过消息转换器获取请求参数- validateIfApplicable:@Validated注解的校验,以后单独将
// RequestResponseBodyMethodProcessor类方法@OverridepublicObjectresolveArgument(MethodParameter parameter,@NullableModelAndViewContainer mavContainer,NativeWebRequest webRequest,@NullableWebDataBinderFactory binderFactory)throwsException{

    parameter = parameter.nestedIfOptional();// 获取参数对象Object arg =readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());String name =Conventions.getVariableNameForParameter(parameter);if(binderFactory !=null){WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);if(arg !=null){// @Validated注解的校验validateIfApplicable(binder, parameter);if(binder.getBindingResult().hasErrors()&&isBindExceptionRequired(binder, parameter)){thrownewMethodArgumentNotValidException(parameter, binder.getBindingResult());}}if(mavContainer !=null){
            mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX+ name, binder.getBindingResult());}}returnadaptArgumentIfNecessary(arg, parameter);}
  • 如果请求参数为空,检查@RequstBodyrequired属性是否为true- true表示@RequstBody注解的参数不能为空- 那么会抛出异常Required request body is missing
// RequestResponseBodyMethodProcessor类方法@Overrideprotected<T>ObjectreadWithMessageConverters(NativeWebRequest webRequest,MethodParameter parameter,Type paramType)throwsIOException,HttpMediaTypeNotSupportedException,HttpMessageNotReadableException{HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);Assert.state(servletRequest !=null,"No HttpServletRequest");ServletServerHttpRequest inputMessage =newServletServerHttpRequest(servletRequest);// 进一步调用方法,通过消息转换器获取请求参数Object arg =readWithMessageConverters(inputMessage, parameter, paramType);// 如果请求为空,检查@RequstBody是否为请求必须参数if(arg ==null&&checkRequired(parameter)){thrownewHttpMessageNotReadableException("Required request body is missing: "+
                parameter.getExecutable().toGenericString(), inputMessage);}return arg;}// @requestBody注解required属性是否为trueprotectedbooleancheckRequired(MethodParameter parameter){RequestBody requestBody = parameter.getParameterAnnotation(RequestBody.class);return(requestBody !=null&& requestBody.required()&&!parameter.isOptional());}
1.2.1、消息转换器

消息转换器接口

  • MediaType类:表示互联网中多媒体数据类型的格式;例如:text/html,text/plain,application/json…
  • canRead方法:检查能否将请求信息转换为mediaType表示的数据类型,这个mediaType是前端页面请求时设定的contentType格式
  • read方法:如果canRead方法返回值为true,则调用read方法将请求信息转换为T类型对象
  • canWrite方法:检查clazz对象是否能转换为mediaType类型,此时的mediaType表示后端想要响应给前端的数据格式
  • write方法:如果canWrite返回值为true,则将T类型的对象写到响应流中,同时指定mediaType类型

在这里插入图片描述

回到上面的readWithMessageConverters方法

  • 首先获取请求头ContentType媒体内容类型,肯定是application/json,默认application/octet-stream
  • 遍历所有的消息转换器,调用canRead方法筛选可以将请求信息转为指定的媒体类型contentType的转换器
  • 然后拿到筛选的消息过滤器转换对象前,这里springmvc留下了扩展点RequestBodyAdvice,可以对请求做一些修改,如加密拦截请求等等
// AbstractMessageConverterMethodArgumentResolver类方法@Nullableprotected<T>ObjectreadWithMessageConverters(HttpInputMessage inputMessage,MethodParameter parameter,Type targetType)throwsIOException,HttpMediaTypeNotSupportedException,HttpMessageNotReadableException{MediaType contentType;boolean noContentType =false;try{// 获取请求头ContentType
        contentType = inputMessage.getHeaders().getContentType();}catch(InvalidMediaTypeException ex){thrownewHttpMediaTypeNotSupportedException(ex.getMessage());}if(contentType ==null){
        noContentType =true;// 默认媒体类型 "application/octet-stream"
        contentType =MediaType.APPLICATION_OCTET_STREAM;}// 获取Controller的Class对象Class<?> contextClass = parameter.getContainingClass();// 获取方法参数的Class对象Class<T> targetClass =(targetType instanceofClass?(Class<T>) targetType :null);if(targetClass ==null){ResolvableType resolvableType =ResolvableType.forMethodParameter(parameter);
        targetClass =(Class<T>) resolvableType.resolve();}HttpMethod httpMethod =(inputMessage instanceofHttpRequest?((HttpRequest) inputMessage).getMethod():null);Object body =NO_VALUE;EmptyBodyCheckingHttpInputMessage message =null;try{
        message =newEmptyBodyCheckingHttpInputMessage(inputMessage);// 遍历所有的消息转换器for(HttpMessageConverter<?> converter :this.messageConverters){Class<HttpMessageConverter<?>> converterType =(Class<HttpMessageConverter<?>>) converter.getClass();GenericHttpMessageConverter<?> genericConverter =(converter instanceofGenericHttpMessageConverter?(GenericHttpMessageConverter<?>) converter :null);// 调用canRead方法,筛选每个消息过滤器是否能将请求信息转为指定的媒体类型contentTypeif(genericConverter !=null? genericConverter.canRead(targetType, contextClass, contentType):(targetClass !=null&& converter.canRead(targetClass, contentType))){if(message.hasBody()){// 获取请求增强器并调用beforeBodyRead方法HttpInputMessage msgToUse =getAdvice().beforeBodyRead(message, parameter, targetType, converterType);// 消息转换器真正将请求信息转为参数对象的方法
                    body =(genericConverter !=null? genericConverter.read(targetType, contextClass, msgToUse):((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));// 获取请求增强器并调用afterBodyRead方法
                    body =getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);}else{
                    body =getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);}break;}}}catch(IOException ex){thrownewHttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);}...return body;}
  • 最终筛选出Jackson消息转换器MappingJackson2HttpMessageConverter

在这里插入图片描述

**genericConverter.canRead

筛选

方法**

// AbstractJackson2HttpMessageConverter类方法@OverridepublicbooleancanRead(Type type,@NullableClass<?> contextClass,@NullableMediaType mediaType){// 判断是否支持传入的mediaTypeif(!canRead(mediaType)){returnfalse;}JavaType javaType =getJavaType(type, contextClass);ObjectMapper objectMapper =selectObjectMapper(javaType.getRawClass(), mediaType);if(objectMapper ==null){returnfalse;}AtomicReference<Throwable> causeRef =newAtomicReference<>();// 判断类能否反序列化,并将错误记录到causeRef中,下面会打印if(objectMapper.canDeserialize(javaType, causeRef)){returntrue;}// 打印causeRef,不能反序列化的错误logWarningIfNecessary(javaType, causeRef.get());returnfalse;}

**genericConverter.read

json反序列化为对象

方法**

// AbstractJackson2HttpMessageConverter类方法@OverridepublicObjectread(Type type,@NullableClass<?> contextClass,HttpInputMessage inputMessage)throwsIOException,HttpMessageNotReadableException{JavaType javaType =getJavaType(type, contextClass);returnreadJavaType(javaType, inputMessage);}

在这里插入图片描述

1.2.2、RequestBodyAdvice请求增强器

**上篇文章SpringMVC源码解析(一):web容器启动流程介绍过,类上有

@ControllerAdvice

注解且实现

RequestBodyAdvice接口

的即为RequestBodyAdvice增强器,主要就是在请求信息转换为参数对象的

前后

做一些

扩展

处理。**

RequestBodyAdvice请求增强器

  • 使用场景:参数的过滤 , 字符的编码 , 第三方的解密等等
publicinterfaceRequestBodyAdvice{// 是否支持,自定义判断条件,如包含某个自定义注解等等// 该方法返回true时,才会进去下面的beforeBodyRead方法booleansupports(MethodParameter methodParameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType);// 请求体解析前处理,一般在此方法中对body数据进行修改HttpInputMessagebeforeBodyRead(HttpInputMessage inputMessage,MethodParameter parameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType)throwsIOException;// 请求体解析后处理,一般直接返回原实例ObjectafterBodyRead(Object body,HttpInputMessage inputMessage,MethodParameter parameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType);// 当body为empty时操作(body什么都不传才算,即使{}也不算空)@NullableObjecthandleEmptyBody(@NullableObject body,HttpInputMessage inputMessage,MethodParameter parameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType);}

回到上面的getAdvice().beforeBodyRead方法

  • 获取所有的请求增强器,调用supports方法- 返回true,表示当前增强器满足条件,接下来调用beforeBodyRead方法`对请求信息做处理- 返回false,表示当前增强器不满足条件,跳过去校验下一个增强器
// RequestResponseBodyAdviceChain类方法@OverridepublicHttpInputMessagebeforeBodyRead(HttpInputMessage request,MethodParameter parameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType)throwsIOException{// 获取所有请求增强器for(RequestBodyAdvice advice :getMatchingAdvice(parameter,RequestBodyAdvice.class)){if(advice.supports(parameter, targetType, converterType)){
            request = advice.beforeBodyRead(request, parameter, targetType, converterType);}}return request;}

getAdvice().afterBodyRead方法

  • 请求参数转换为对象以后的处理,这时候可以对参数对象做一些扩展处理
  • 与上面beforeBodyRead方法一样,先调用supports校验是否支持,再调用afterBodyRead处理
// RequestResponseBodyAdviceChain类方法@OverridepublicObjectafterBodyRead(Object body,HttpInputMessage inputMessage,MethodParameter parameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType){for(RequestBodyAdvice advice :getMatchingAdvice(parameter,RequestBodyAdvice.class)){if(advice.supports(parameter, targetType, converterType)){
            body = advice.afterBodyRead(body, inputMessage, parameter, targetType, converterType);}}return body;}

getAdvice().handleEmptyBody方法

  • 请求body什么都没有才会进入此方法
// RequestResponseBodyAdviceChain类方法@Override@NullablepublicObjecthandleEmptyBody(@NullableObject body,HttpInputMessage inputMessage,MethodParameter parameter,Type targetType,Class<?extendsHttpMessageConverter<?>> converterType){for(RequestBodyAdvice advice :getMatchingAdvice(parameter,RequestBodyAdvice.class)){if(advice.supports(parameter, targetType, converterType)){
            body = advice.handleEmptyBody(body, inputMessage, parameter, targetType, converterType);}}return body;}

2、执行Controller具体逻辑方法

  • getBean获取的Controller对象,method.invoke(obj,args)标准反射调用方法
// InvocableHandlerMethod类方法@NullableprotectedObjectdoInvoke(Object... args)throwsException{Method method =getBridgedMethod();try{if(KotlinDetector.isSuspendingFunction(method)){returnCoroutinesUtils.invokeSuspendingFunction(method,getBean(), args);}// 反射调用方法return method.invoke(getBean(), args);}catch(IllegalArgumentException ex){...}}

3、返回对象转为响应信息(json)

  • 获取支持处理当前返回值的处理器,并调用handleReturnValue处理方法
// HandlerMethodReturnValueHandlerComposite类方法@OverridepublicvoidhandleReturnValue(@NullableObject returnValue,MethodParameter returnType,ModelAndViewContainer mavContainer,NativeWebRequest webRequest)throwsException{// 获取方法返回值处理器HandlerMethodReturnValueHandler handler =selectHandler(returnValue, returnType);if(handler ==null){thrownewIllegalArgumentException("Unknown return value type: "+ returnType.getParameterType().getName());}// 处理方法
    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}

3.1、获取返回值处理器

  • 遍历所有的返回值处理器,通过调用处理器的supportsReturnType方法筛选
// HandlerMethodReturnValueHandlerComposite类方法@NullableprivateHandlerMethodReturnValueHandlerselectHandler(@NullableObject value,MethodParameter returnType){boolean isAsyncValue =isAsyncReturnValue(value, returnType);// 遍历所有的返回值处理器for(HandlerMethodReturnValueHandler handler :this.returnValueHandlers){// 排除异步处理器,不用管if(isAsyncValue &&!(handler instanceofAsyncHandlerMethodReturnValueHandler)){continue;}// 通过调用处理器的supportsReturnType方法筛选if(handler.supportsReturnType(returnType)){return handler;}}returnnull;}

在这里插入图片描述

**查看

RequestResponseBodyMethodProcessor

的筛选方法

handleReturnValue

**

  • supportsReturnType方法简单明了,方法类上包含注解@ResponseBody即可
@OverridepublicbooleansupportsReturnType(MethodParameter returnType){return(AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),ResponseBody.class)||
            returnType.hasMethodAnnotation(ResponseBody.class));}

3.2、返回值处理器处理方法

// RequestResponseBodyMethodProcessor类方法@OverridepublicvoidhandleReturnValue(@NullableObject returnValue,MethodParameter returnType,ModelAndViewContainer mavContainer,NativeWebRequest webRequest)throwsIOException,HttpMediaTypeNotAcceptableException,HttpMessageNotWritableException{

    mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage =createInputMessage(webRequest);ServletServerHttpResponse outputMessage =createOutputMessage(webRequest);// 即使返回值为空,也要尝试。ResponseBodyAdvice可能会参与其中。writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}

在这里插入图片描述

3.2.1、消息转换器

**这里用的消息转换器与获取请求参数里的转换器一样,都是

MappingJackson2HttpMessageConverter

。之前转化器是需要将请求信息body里的json字符串转换(

反序列化

)为对象;这里的转换器是将对象转换(

序列化

)对json字符串。**

**genericConverter.canWrite

筛选

方法**

// AbstractGenericHttpMessageConverter类方法@OverridepublicbooleancanWrite(@NullableType type,Class<?> clazz,@NullableMediaType mediaType){returncanWrite(clazz, mediaType);}// AbstractJackson2HttpMessageConverter类方法@OverridepublicbooleancanWrite(Class<?> clazz,@NullableMediaType mediaType){// 判断是否支持传入的mediaTypeif(!canWrite(mediaType)){returnfalse;}if(mediaType !=null&& mediaType.getCharset()!=null){Charset charset = mediaType.getCharset();if(!ENCODINGS.containsKey(charset.name())){returnfalse;}}ObjectMapper objectMapper =selectObjectMapper(clazz, mediaType);if(objectMapper ==null){returnfalse;}AtomicReference<Throwable> causeRef =newAtomicReference<>();// 判断对象是否能序列化为json字符串,并将错误记录到causeRef中,下面会打印if(objectMapper.canSerialize(clazz, causeRef)){returntrue;}// 打印causeRef,不能序列化的错误logWarningIfNecessary(clazz, causeRef.get());returnfalse;}

**genericConverter.write

对象序列化为json

方法**

// AbstractGenericHttpMessageConverter类方法@Overridepublicfinalvoidwrite(finalT t,@NullablefinalType type,@NullableMediaType contentType,HttpOutputMessage outputMessage)throwsIOException,HttpMessageNotWritableException{// 设置默认请求头finalHttpHeaders headers = outputMessage.getHeaders();addDefaultHeaders(headers, t, contentType);if(outputMessage instanceofStreamingHttpOutputMessage){StreamingHttpOutputMessage streamingOutputMessage =(StreamingHttpOutputMessage) outputMessage;
        streamingOutputMessage.setBody(outputStream ->writeInternal(t, type,newHttpOutputMessage(){@OverridepublicOutputStreamgetBody(){return outputStream;}@OverridepublicHttpHeadersgetHeaders(){return headers;}}));}else{// jackson序列化方法writeInternal(t, type, outputMessage);
        outputMessage.getBody().flush();}}
  • 核心方法:对象转换为json字符串并写入输出流

在这里插入图片描述

3.2.2、ResponseBodyAdvice响应增强器

**上篇文章SpringMVC源码解析(一):web容器启动流程介绍过,类上有

@ControllerAdvice

注解且实现

ResponseBodyAdvice接口

的即为ResponseBodyAdvice增强器,主要就是在返回对象转换响应信息

做一些

扩展

处理。**

ResponseBodyAdvice响应增强器

  • 使用场景:对response数据统一封装或者加密等操作
publicinterfaceResponseBodyAdvice<T>{// 是否支持,自定义判断条件// 该方法返回true时,才会进去下面的 beforeBodyWrite方法booleansupports(MethodParameter returnType,Class<?extendsHttpMessageConverter<?>> converterType);// 响应写入之前调用@NullableTbeforeBodyWrite(@NullableT body,MethodParameter returnType,MediaType selectedContentType,Class<?extendsHttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request,ServerHttpResponse response);}

回到上面的getAdvice().beforeBodyWrite方法

  • 遍历所有的响应增强器,调用supports方法筛选支持的增强,然后调用增强方法beforeBodyWrite
  • 此时beforeBodyWrite方法拿到的body即为方法返回值,还没有序列化,我们可以对返回值扩展处理
// RequestResponseBodyAdviceChain类方法@Override@NullablepublicObjectbeforeBodyWrite(@NullableObject body,MethodParameter returnType,MediaType contentType,Class<?extendsHttpMessageConverter<?>> converterType,ServerHttpRequest request,ServerHttpResponse response){returnprocessBody(body, returnType, contentType, converterType, request, response);}@Nullableprivate<T>ObjectprocessBody(@NullableObject body,MethodParameter returnType,MediaType contentType,Class<?extendsHttpMessageConverter<?>> converterType,ServerHttpRequest request,ServerHttpResponse response){// 遍历所有的响应增强器,调用supports方法,筛选支持的增强器for(ResponseBodyAdvice<?> advice :getMatchingAdvice(returnType,ResponseBodyAdvice.class)){if(advice.supports(returnType, converterType)){
            body =((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
                    contentType, converterType, request, response);}}return body;}

四、拦截器

  • 文章第一节获取执行器链HandlerExecutionChain里面就包含了拦截器集合,如下
// 执行器链对象,主要就是两个属性handler:Handler对象,interceptorList:拦截器集合publicclassHandlerExecutionChain{privatefinalObject handler;privatefinalList<HandlerInterceptor> interceptorList =newArrayList<>();// 构造方法publicHandlerExecutionChain(Object handler){this(handler,(HandlerInterceptor[])null);}...}

拦截器接口

publicinterfaceHandlerInterceptor{/**
     * 执行处理程序之前的拦截点
     */defaultbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{returntrue;}/**
     * 成功执行处理程序后的拦截点
     */defaultvoidpostHandle(HttpServletRequest request,HttpServletResponse response,Object handler,@NullableModelAndView modelAndView)throwsException{}/**
     * 请求处理完成后的回调,即渲染视图后
     */defaultvoidafterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,@NullableException ex)throwsException{}}
  • 拦截器的处理的位置- 前置处理:执行方法- 后置处理:执行方法- 最终处理:最后必执行

在这里插入图片描述

1、执行拦截器preHandle方法

  • 遍历所有的拦截器,调用preHandle方法- 返回true,则正常遍历所有的拦截器,并调用所有拦截器的preHandle方法- 返回false,则不再循环遍历后面的拦截器,只会调用当前拦截器的最终方法afterCompletion,并且Handler都不再执行,直接返回
// HandlerExecutionChain类方法booleanapplyPreHandle(HttpServletRequest request,HttpServletResponse response)throwsException{for(int i =0; i <this.interceptorList.size(); i++){// 遍历所有的拦截器,调用preHandle方法HandlerInterceptor interceptor =this.interceptorList.get(i);if(!interceptor.preHandle(request, response,this.handler)){triggerAfterCompletion(request, response,null);returnfalse;}// 拦截器集合索引下标记录this.interceptorIndex = i;}returntrue;}

2、执行拦截器postHandle方法

  • 正常遍历调用,但是是根据拦截器顺序的倒序遍历执行postHandle方法
// HandlerExecutionChain类方法voidapplyPostHandle(HttpServletRequest request,HttpServletResponse response,@NullableModelAndView mv)throwsException{for(int i =this.interceptorList.size()-1; i >=0; i--){HandlerInterceptor interceptor =this.interceptorList.get(i);
        interceptor.postHandle(request, response,this.handler, mv);}}

3、执行拦截器afterCompletion方法

  • interceptorIndex记录的是几个执行过preHandle方法的拦截器的数量
  • 这里也是倒序调用afterCompletion方法
voidtriggerAfterCompletion(HttpServletRequest request,HttpServletResponse response,@NullableException ex){for(int i =this.interceptorIndex; i >=0; i--){HandlerInterceptor interceptor =this.interceptorList.get(i);try{
            interceptor.afterCompletion(request, response,this.handler, ex);}catch(Throwable ex2){
            logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);}}}

一般来说拦截器preHandle的方法会返回true(表示放行),那么对于拦截器三个方法执行顺序即为:123 321 321

五、异常处理器

**之前文章SpringMVC源码解析(一):web容器启动流程有介绍,筛选异常处理器即

类上@ControllerAdvice

方法上@ExceptionHandler

。**

@ControllerAdvicepublicclassExceptionController{@ExceptionHandler(value ={Exception.class})@ResponseBodypublicResponseEntity<String>exceptionHandler(Exception e){returnResponseEntity.status(500).body("系统异常");}}
  • 异常处理器触发位置

在这里插入图片描述

// DispatcherServlet类方法privatevoidprocessDispatchResult(HttpServletRequest request,HttpServletResponse response,@NullableHandlerExecutionChain mappedHandler,@NullableModelAndView mv,@NullableException exception)throwsException{boolean errorView =false;if(exception !=null){if(exception instanceofModelAndViewDefiningException){
            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);}}...// 执行拦截器的最终处理if(mappedHandler !=null){
        mappedHandler.triggerAfterCompletion(request, response,null);}}
  • 遍历所有的异常处理器,调用resolveException方法,返回结果不为null,即跳出循环直接返回
@NullableprotectedModelAndViewprocessHandlerException(HttpServletRequest request,HttpServletResponse response,@NullableObject handler,Exception ex)throwsException{...// 遍历异常处理器ModelAndView exMv =null;if(this.handlerExceptionResolvers !=null){for(HandlerExceptionResolver resolver :this.handlerExceptionResolvers){
            exMv = resolver.resolveException(request, response, handler, ex);if(exMv !=null){break;}}}// 最后抛出异常throw ex;}

总结

**JavaWeb是每一个业务逻辑就需要构建一个

Servelt

,Tomcat解析请求也是通过请求路径映射找到对于的Servelt程序。而SpringMVC只构建

一个

Servelt,那就是

DispatcherServlet

,这里Servelt接收所有的请求,然后再根据请求路径分发出去找到对于的Controller方法。**

  • 获取执行器链(包括Handler和拦截器)- 截取请求uri获取@RequestMapping注解映射的路径- 项目启动时,将所有@Controller#Method @RequestMapping对应关系添加到map集合中- 这里通过请求路径可以获取到具体的Controller#Method- 对于拦截器也是在项目启动阶段,将所有拦截器根据排序放到集合中,这里直接拿来即可

**定义Handler的方式有很多,早期有实现Controller、HttpRequestHandler接口,现在常用的@Controller方式,不同的Handler方式生成请求和Handler的映射的方法就不同,这时候抽象出来

HandlerMapping

(根据request请求匹配/映射上能够处理当前request的Handler),上面说的获取执行器链获取Handler就是专门处理@Controller的HandlerMapping,这样就出现了不同实现的HandlerMapping。**
**不同Handler调用具体实现逻辑的方法也不同,@Controller方式直接调用记录的类的Method即可,而其他实现接口的方式这是调用此接口实现类的重写handleRequest方法,这时候抽象出来

HandlerAdapter

,不同HandlerAdapter处理不同Handler。**

  • 执行Handler前,即调用Controller具体方法,需要将方法的参数都获取到- 对于我们常见的@RequestBody@RequestParam@PathVariable注解SpringMVC都内置的参数解析器- @RequestBody的参数解析需要用到消息转换器(请求信息转换为java对象),SpringMVC内置的Byte、String等转换器,也可以通过导包导入jacksonfastjson转换器- 对于json转换器就是将请求信息里body的json字符串反序列化为java对象- 在转换对象前后,SpringMVC留下了扩展点,请求增强器RequestponseBodyAdvice,可以对转换前的body和转换后的对象做扩展处理
  • 执行Handler就很简单了,直接method.invoke(obj,args)反射调用方法即可
  • 执行Handler后,使用返回值处理器对象返回值做处理了- 对于类上或方法上有@ResponseBody的,使用消息转换器将java对象序列化为json字符串(以后会传给前端)- 同样也是再转换前,SpringMVC留下了扩展点,响应增强器ResponseBodyAdvice,可以对方法返回值做扩展处理再序列化
  • 再说下我们常用的扩展点,拦截器,在方法执行前后最后无论是否抛异常都会执行的三个位置,都可以做扩展处理
标签: java spring servlet

本文转载自: https://blog.csdn.net/qq_35512802/article/details/140209209
版权归原作者 冬天vs不冷 所有, 如有侵权,请联系我们删除。

“SpringMVC源码解析(二):请求执行流程”的评论:

还没有评论