SpringMVC源码系列文章
SpringMVC源码解析(一):web容器启动流程
SpringMVC源码解析(二):请求执行流程
目录
- 前言
- DispatcherServlet入口
- 一、获取HandlerExcutionChain(包括Handler)
- 1、获取Handler
- 1.1、通过request获取查找路径
- 1.2、通过查找路径获取HandlerMethod
- 2、获取执行链(包括Handler、拦截器)
- 二、获取适配器
- 三、执行Handler(🔥重点)
- 1、获取请求参数
- 1.1、获取参数解析器
- 1.2、解析@RequestBodey请求参数
- 1.2.1、消息转换器
- 1.2.2、RequestBodyAdvice请求增强器
- 2、执行Controller具体逻辑方法
- 3、返回对象转为响应信息(json)
- 3.1、获取返回值处理器
- 3.2、返回值处理器处理方法
- 3.2.1、消息转换器
- 3.2.2、ResponseBodyAdvice响应增强器
- 四、拦截器
- 1、执行拦截器preHandle方法
- 2、执行拦截器postHandle方法
- 3、执行拦截器afterCompletion方法
- 五、异常处理器
- 总结
前言
前文中我们介绍了SpringMVC容器的启动,包括前端控制器DispatcherServlet对象的创建,过滤器添加到Tomcat容器的过滤器集合中,将所有拦截器、跨域配置、消息转换器等配置统一添加到各自集合中,解析@RequestMapping注解生成请求路径和Controller方法的映射map。本章来研究下请求的执行过程
。
说到请求过程,那么得先说下入口在哪里?入口肯定是统一分发请求给处理程序的DispatcherServlet
,DispatcherServlet归根结底也是Servlet。Tomcat通过请求Mapping映射和Servelt对应关系找到Servelt,调用Servelt之前会执行过滤器链,所有过滤器放行才会走到Servelt真正的执行逻辑。
- 依照常见的post请求为例
// 接受User对象并返回
@PostMapping("/test")
@ResponseBody
public User test(@RequestBody User 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类方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, 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;throw new NestedServletException("Request processing failed", ex);}finally {...}
}
- 主要方法
doDispatch(request, response)
// DispatcherServlet类方法
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {// ... 设置一堆Attribute属性try {doDispatch(request, response);}finally {...}
}
核心方法
doDispatch
- 这个方法包含了SpringMVC的整个执行流程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 调用拦截器的前置方法preHandleif (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 执行handler方法// 需要跳转页面这里才会返回ModelAndView对象,否则@ResponseBody返回对象这里返回nullmv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 调用拦截器的后置方法postHandlemappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {//从4.3开始,我们也在处理处理程序方法抛出的错误,//使它们可用于@ExceptionHandler方法和其他场景。dispatchException = new NestedServletException("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,new NestedServletException("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类方法
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}
如下这三个HandlerMapping是web容器启动时候加载的,上篇文章SpringMVC源码解析(一):web容器启动流程有具体介绍。三个轮流调用getHandler方法,而HandlerMapping也有顺序的,RequestMappingHanderMapping
排序为0优先级最高,而它也是处理@RequestMapping
注解的映射关系的映射器。
调用抽象类
的方法,那么上面看到的三个HandlerMapping应该都会调用此方法,而这里肯定有一些核心的不同的方法实现在不同的HandlerMapping具体实现类中,典型的模板方法
设计模式。
// AbstractHandlerMapping类方法
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {// 获取HandlerObject handler = getHandlerInternal(request);if (handler == null) {handler = getDefaultHandler();}if (handler == null) {return null;}// handler为bean的名称// 这种Controller应该是实现Controler、HttpRequestHandler接口的方式// 以前的老实现方式,暂不研究if (handler instanceof String) {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
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);try {// 核心方法调用父类的getHandlerInternal方法return super.getHandlerInternal(request);}finally {ProducesRequestCondition.clearMediaTypesAttribute(request);}
}
- 通过request获取
查找路径
- 通过查找路径获取
HandlerMethod
// AbstractHandlerMethodMapping类方法
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {// 通过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类方法
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<Match> matches = new ArrayList<>();// 通过查找路径获取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 {return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);}
}
2、获取执行链(包括Handler、拦截器)
我们自定义的拦截器统一用MappedInterceptor
这个拦截器包装了一层,为了统一调用matcher
方法,匹配此拦截器请求是否拦截本次请求,如果是则会添加到拦截器链中。
// AbstractHandlerMapping类方法
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {// 创建HandlerExecutionChain对象HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));// 遍历所有的拦截器,这拦截器是web容器启动时候解析加载的的for (HandlerInterceptor interceptor : this.adaptedInterceptors) {// 我们自定义的拦截器统一用MappedInterceptor这个拦截器包装了一层// 为了统一的匹配方法,下面调用machesif (interceptor instanceof MappedInterceptor) {MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;// matcher匹配当前请求路径是否符合拦截器的拦截请求if (mappedInterceptor.matches(request)) {chain.addInterceptor(mappedInterceptor.getInterceptor());}}else {chain.addInterceptor(interceptor);}}return chain;
}// 执行器链对象,主要就是两个属性handler:Handler对象,interceptorList:拦截器集合
public class HandlerExecutionChain {private final Object handler;private final List<HandlerInterceptor> interceptorList = new ArrayList<>();// 构造方法public HandlerExecutionChain(Object handler) {this(handler, (HandlerInterceptor[]) null);}...
}
- 拦截器链最终的结果
二、获取适配器
看下HandlerAdapter接口
public interface HandlerAdapter {/*** 因为有多个HandlerMapping和HandlerAdapter* 对于HandlerAdapter是否支持对应的HandlerMapping,通过此方法判断*/boolean supports(Object handler);/*** 具体调用Hangder的方法*/@NullableModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
因为不同的Hander
(@RequestMapping、实现Controller接口、实现HttpRequestHandler接口)对应的HandlerAdapter
(适配器)不一样,通过HandlerAdapter的supports
方法判断当前HandlerAdapter是否支持此次请求的Hander
。
// DispatcherServlet类方法
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("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类方法
@Override
public final boolean supports(Object handler) {return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}// RequestMappingHandlerAdapter类方法
@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {return true;
}
由上面HandlerAdapter接口可以猜到,RequestMappingHandlerAdapter
适配器就是我们需要的,之后会通过handle方法去执行Hangder方法
即调用Controller#Method
。
三、执行Handler(🔥重点)
AbstractHandlerMethodAdapter类的handle方法即重写HandlerAdapter的handle方法,所有的HandlerAdapter执行Hangdler都会进入此方法,而具体的方法实现又要调用HandlerAdapter的实现类,如下,实现类就在RequestMappingHandlerAdapter
类的handleInternal
方法。
// AbstractHandlerMethodAdapter类方法
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);
}
- 执行Handle方法内又包括
解析请求
,执行真正逻辑
,解析响应
- 执行Handler并获取返回值,处理响应,如对象转化为json
// ServletInvocableHandlerMethod类方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 执行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;}}else if (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接口
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 获取请求参数Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Arguments: " + Arrays.toString(args));}// 执行真正逻辑return doInvoke(args);
}
1、获取请求参数
拿到具体Controller的Method
方法参数,遍历所有参数寻找支持每个参数类型的参数解析器
,解析参数并返回。
// InvocableHandlerMethod类方法,实现HandlerMethod接口
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 从HandlerMethod中获取参数信息// 之前项目启动就加载了Handler,里面包含了具体要执行的Controller的MethodMethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}// 遍历所有的参数Object[] args = new Object[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)) {throw new IllegalStateException(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类方法
@Override
public boolean supportsParameter(MethodParameter parameter) {return getArgumentResolver(parameter) != null;
}@Nullable
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;
}
- 参数解析器集合
HandlerMethodArgumentResolver argumentResolvers
中一共有27个- 几乎每个注解就是一个解析器,如
@RequestParam
、@PathVariable
等等
- 几乎每个注解就是一个解析器,如
- 参数解析器集合中下标7就是我们常用的
@RequestBody
注解参数解析器
- supportsParameter方法简单明了,参数包含注解
@RequestBody
即可
// RequestResponseBodyMethodProcessor类方法
@Override
public boolean supportsParameter(MethodParameter parameter) {return parameter.hasParameterAnnotation(RequestBody.class);
}
1.2、解析@RequestBodey请求参数
// HandlerMethodArgumentResolverComposite类方法
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 获取参数解析器,此时上面已经筛查出来,放到argumentResolverCache缓存中HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unsupported parameter type [" +parameter.getParameterType().getName() + "]. supportsParameter should be called first.");}// 解析方法return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
- 调用注解解析器
RequestResponseBodyMethodProcessor
的resolveArgument
方法- readWithMessageConverters:通过
消息转换器
获取请求参数 - validateIfApplicable:
@Validated
注解的校验,以后单独将
- readWithMessageConverters:通过
// RequestResponseBodyMethodProcessor类方法
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {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)) {throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());}}if (mavContainer != null) {mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());}}return adaptArgumentIfNecessary(arg, parameter);
}
- 如果请求参数为空,检查
@RequstBody
的required
属性是否为true
- true表示@RequstBody注解的参数不能为空
- 那么会抛出异常
Required request body is missing
// RequestResponseBodyMethodProcessor类方法
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);Assert.state(servletRequest != null, "No HttpServletRequest");ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);// 进一步调用方法,通过消息转换器获取请求参数Object arg = readWithMessageConverters(inputMessage, parameter, paramType);// 如果请求为空,检查@RequstBody是否为请求必须参数if (arg == null && checkRequired(parameter)) {throw new HttpMessageNotReadableException("Required request body is missing: " +parameter.getExecutable().toGenericString(), inputMessage);}return arg;
}
// @requestBody注解required属性是否为true
protected boolean checkRequired(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类方法
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {MediaType contentType;boolean noContentType = false;try {// 获取请求头ContentTypecontentType = inputMessage.getHeaders().getContentType();}catch (InvalidMediaTypeException ex) {throw new HttpMediaTypeNotSupportedException(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 instanceof Class ? (Class<T>) targetType : null);if (targetClass == null) {ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);targetClass = (Class<T>) resolvableType.resolve();}HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);Object body = NO_VALUE;EmptyBodyCheckingHttpInputMessage message = null;try {message = new EmptyBodyCheckingHttpInputMessage(inputMessage);// 遍历所有的消息转换器for (HttpMessageConverter<?> converter : this.messageConverters) {Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();GenericHttpMessageConverter<?> genericConverter =(converter instanceof GenericHttpMessageConverter ? (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) {throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);}...return body;
}
- 最终筛选出Jackson消息转换器
MappingJackson2HttpMessageConverter
genericConverter.canRead
筛选
方法
// AbstractJackson2HttpMessageConverter类方法
@Override
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {// 判断是否支持传入的mediaTypeif (!canRead(mediaType)) {return false;}JavaType javaType = getJavaType(type, contextClass);ObjectMapper objectMapper = selectObjectMapper(javaType.getRawClass(), mediaType);if (objectMapper == null) {return false;}AtomicReference<Throwable> causeRef = new AtomicReference<>();// 判断类能否反序列化,并将错误记录到causeRef中,下面会打印if (objectMapper.canDeserialize(javaType, causeRef)) {return true;}// 打印causeRef,不能反序列化的错误logWarningIfNecessary(javaType, causeRef.get());return false;
}
genericConverter.read
json反序列化为对象
方法
// AbstractJackson2HttpMessageConverter类方法
@Override
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {JavaType javaType = getJavaType(type, contextClass);return readJavaType(javaType, inputMessage);
}
1.2.2、RequestBodyAdvice请求增强器
上篇文章SpringMVC源码解析(一):web容器启动流程介绍过,类上有@ControllerAdvice
注解且实现RequestBodyAdvice接口
的即为RequestBodyAdvice增强器,主要就是在请求信息转换为参数对象的前后
做一些扩展
处理。
RequestBodyAdvice请求增强器
- 使用场景:参数的过滤 , 字符的编码 , 第三方的解密等等
public interface RequestBodyAdvice {// 是否支持,自定义判断条件,如包含某个自定义注解等等// 该方法返回true时,才会进去下面的beforeBodyRead方法boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType);// 请求体解析前处理,一般在此方法中对body数据进行修改HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;// 请求体解析后处理,一般直接返回原实例Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType);// 当body为empty时操作(body什么都不传才算,即使{}也不算空)@NullableObject handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType);}
回到上面的getAdvice().beforeBodyRead方法
- 获取所有的请求增强器,调用supports方法
- 返回true,表示当前增强器满足条件,接下来调用
beforeBodyRead
方法`对请求信息做处理 - 返回false,表示当前增强器不满足条件,跳过去校验下一个增强器
- 返回true,表示当前增强器满足条件,接下来调用
// RequestResponseBodyAdviceChain类方法
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {// 获取所有请求增强器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类方法
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> 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
@Nullable
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> 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类方法
@Nullable
protected Object doInvoke(Object... args) throws Exception {Method method = getBridgedMethod();try {if (KotlinDetector.isSuspendingFunction(method)) {return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);}// 反射调用方法return method.invoke(getBean(), args);}catch (IllegalArgumentException ex) {...}
}
3、返回对象转为响应信息(json)
- 获取支持处理当前返回值的处理器,并调用
handleReturnValue
处理方法
// HandlerMethodReturnValueHandlerComposite类方法
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 获取方法返回值处理器HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}// 处理方法handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
3.1、获取返回值处理器
- 遍历所有的返回值处理器,通过调用处理器的
supportsReturnType
方法筛选
// HandlerMethodReturnValueHandlerComposite类方法
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {boolean isAsyncValue = isAsyncReturnValue(value, returnType);// 遍历所有的返回值处理器for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {// 排除异步处理器,不用管if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {continue;}// 通过调用处理器的supportsReturnType方法筛选if (handler.supportsReturnType(returnType)) {return handler;}}return null;
}
查看
RequestResponseBodyMethodProcessor
的筛选方法handleReturnValue
- supportsReturnType方法简单明了,
方法
或类上
包含注解@ResponseBody
即可
@Override
public boolean supportsReturnType(MethodParameter returnType) {return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||returnType.hasMethodAnnotation(ResponseBody.class));
}
3.2、返回值处理器处理方法
// RequestResponseBodyMethodProcessor类方法
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, 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类方法
@Override
public boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType) {return canWrite(clazz, mediaType);
}
// AbstractJackson2HttpMessageConverter类方法
@Override
public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {// 判断是否支持传入的mediaTypeif (!canWrite(mediaType)) {return false;}if (mediaType != null && mediaType.getCharset() != null) {Charset charset = mediaType.getCharset();if (!ENCODINGS.containsKey(charset.name())) {return false;}}ObjectMapper objectMapper = selectObjectMapper(clazz, mediaType);if (objectMapper == null) {return false;}AtomicReference<Throwable> causeRef = new AtomicReference<>();// 判断对象是否能序列化为json字符串,并将错误记录到causeRef中,下面会打印if (objectMapper.canSerialize(clazz, causeRef)) {return true;}// 打印causeRef,不能序列化的错误logWarningIfNecessary(clazz, causeRef.get());return false;
}
genericConverter.write
对象序列化为json
方法
// AbstractGenericHttpMessageConverter类方法
@Override
public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {// 设置默认请求头final HttpHeaders headers = outputMessage.getHeaders();addDefaultHeaders(headers, t, contentType);if (outputMessage instanceof StreamingHttpOutputMessage) {StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {@Overridepublic OutputStream getBody() {return outputStream;}@Overridepublic HttpHeaders getHeaders() {return headers;}}));}else {// jackson序列化方法writeInternal(t, type, outputMessage);outputMessage.getBody().flush();}
}
- 核心方法:对象转换为json字符串并写入
输出流
中
3.2.2、ResponseBodyAdvice响应增强器
上篇文章SpringMVC源码解析(一):web容器启动流程介绍过,类上有@ControllerAdvice
注解且实现ResponseBodyAdvice接口
的即为ResponseBodyAdvice增强器,主要就是在返回对象转换响应信息前
做一些扩展
处理。
ResponseBodyAdvice响应增强器
- 使用场景:对response数据统一封装或者加密等操作
public interface ResponseBodyAdvice<T> {// 是否支持,自定义判断条件// 该方法返回true时,才会进去下面的 beforeBodyWrite方法boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);// 响应写入之前调用@NullableT beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response);}
回到上面的getAdvice().beforeBodyWrite方法
- 遍历所有的响应增强器,调用
supports
方法筛选支持的增强,然后调用增强方法beforeBodyWrite
- 此时beforeBodyWrite方法拿到的
body即为方法返回值
,还没有序列化,我们可以对返回值
做扩展处理
了
// RequestResponseBodyAdviceChain类方法
@Override
@Nullable
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,Class<? extends HttpMessageConverter<?>> converterType,ServerHttpRequest request, ServerHttpResponse response) {return processBody(body, returnType, contentType, converterType, request, response);
}
@Nullable
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,Class<? extends HttpMessageConverter<?>> 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:拦截器集合
public class HandlerExecutionChain {private final Object handler;private final List<HandlerInterceptor> interceptorList = new ArrayList<>();// 构造方法public HandlerExecutionChain(Object handler) {this(handler, (HandlerInterceptor[]) null);}...
}
拦截器接口
public interface HandlerInterceptor {/*** 执行处理程序之前的拦截点*/default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}/*** 成功执行处理程序后的拦截点*/default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable ModelAndView modelAndView) throws Exception {}/*** 请求处理完成后的回调,即渲染视图后*/default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,@Nullable Exception ex) throws Exception {}
}
- 拦截器的处理的位置
- 前置处理:执行方法
前
- 后置处理:执行方法
后
- 最终处理:
最后
必执行
- 前置处理:执行方法
1、执行拦截器preHandle方法
- 遍历所有的拦截器,调用preHandle方法
- 返回true,则正常遍历所有的拦截器,并调用所有拦截器的
preHandle
方法 - 返回false,则不再循环遍历后面的拦截器,只会调用当前拦截器的最终方法
afterCompletion
,并且Handler都不再执行
,直接返回
- 返回true,则正常遍历所有的拦截器,并调用所有拦截器的
// HandlerExecutionChain类方法
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {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);return false;}// 拦截器集合索引下标记录this.interceptorIndex = i;}return true;
}
2、执行拦截器postHandle方法
- 正常遍历调用,但是是根据拦截器顺序的
倒序
遍历执行postHandle
方法
// HandlerExecutionChain类方法
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)throws Exception {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
方法
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception 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
。
@ControllerAdvice
public class ExceptionController {@ExceptionHandler(value = {Exception.class})@ResponseBodypublic ResponseEntity<String> exceptionHandler(Exception e) {return ResponseEntity.status(500).body("系统异常");}
}
- 异常处理器触发位置
// DispatcherServlet类方法
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;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);}}...// 执行拦截器的最终处理if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}
}
- 遍历所有的异常处理器,调用
resolveException
方法,返回结果不为null,即跳出循环直接返回
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {...// 遍历异常处理器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等转换器,也可以通过导包导入
jackson
和fastjson
转换器 - 对于json转换器就是将请求信息里body的
json字符串反序列化为java对象
- 在转换对象前后,SpringMVC留下了扩展点,
请求增强器RequestponseBodyAdvice
,可以对转换前
的body和转换后的对象做扩展处理
- 对于我们常见的
- 执行Handler就很简单了,直接method.invoke(obj,args)反射调用方法即可
- 执行Handler后,使用返回值处理器对象返回值做处理了
- 对于类上或方法上有
@ResponseBody
的,使用消息转换器将java对象序列化为json字符串
(以后会传给前端) - 同样也是再转换前,SpringMVC留下了扩展点,
响应增强器ResponseBodyAdvice
,可以对方法返回值做扩展处理再序列化
- 对于类上或方法上有
- 再说下我们常用的扩展点,拦截器,在方法执行
前后
及最后
无论是否抛异常都会执行的三个位置
,都可以做扩展处理