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

冬天vs不冷 2024-08-02 17:07:11 阅读 67

SpringMVC源码系列文章

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

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


目录

前言DispatcherServlet入口一、获取HandlerExcutionChain(包括Handler)1、获取Handler1.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。本章来研究下<code>请求的执行过程。

说到请求过程,那么得先说下入口在哪里?入口肯定是统一分发请求给处理程序的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方法,只要找到<code>DispatcherServelt重写的doPost方法就是入口了

在这里插入图片描述

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

DispatcherServlet入口

DispatcherServlet的类图如下:

在这里插入图片描述

从doPost到doDispatch方法

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

在这里插入图片描述

主要方法<code>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;

}

}

// 调用拦截器的前置方法preHandle

if (!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 = new NestedServletException("Handler dispatch failed", err);

}

// 处理结果

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

}

catch (Exception ex) {

// 调用拦截器请求处理完成后的回调triggerAfterCompletion

triggerAfterCompletion(processedRequest, response, mappedHandler, ex);

}

catch (Throwable err) {

// 调用拦截器请求处理完成后的回调triggerAfterCompletion

triggerAfterCompletion(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注解的映射关系的映射器。

在这里插入图片描述

调用<code>抽象类的方法,那么上面看到的三个HandlerMapping应该都会调用此方法,而这里肯定有一些核心的不同的方法实现在不同的HandlerMapping具体实现类中,典型的模板方法设计模式。

// AbstractHandlerMapping类方法

@Override

@Nullable

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

// 获取Handler

Object 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 {

// 通过查找路径获取HandlerMethod

HandlerMethod 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容器启动流程注册的<code>两个map获取数据。

在这里插入图片描述

<code>// AbstractHandlerMethodMapping类方法

@Nullable

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {

List<Match> matches = new ArrayList<>();

// 通过查找路径获取RequestMappingInfo

List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);

if (directPathMatches != null) {

// 通过RequestMappingInfo获取HandlerMethod

addMatchingMappings(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、拦截器)

我们自定义的拦截器统一用<code>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这个拦截器包装了一层

// 为了统一的匹配方法,下面调用maches

if (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接口

<code>public interface HandlerAdapter {

/**

* 因为有多个HandlerMapping和HandlerAdapter

* 对于HandlerAdapter是否支持对应的HandlerMapping,通过此方法判断

*/

boolean supports(Object handler);

/**

* 具体调用Hangder的方法

*/

@Nullable

ModelAndView 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

<code>// 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 {

// 处理响应,返回对象转换响应信息,如对象转化为json

this.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的Method

MethodParameter[] 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方法简单明了,参数包含注解<code>@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);

}

调用注解解析器RequestResponseBodyMethodProcessorresolveArgument方法

readWithMessageConverters:通过消息转换器获取请求参数validateIfApplicable:@Validated注解的校验,以后单独将

// 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);

}

如果请求参数为空,检查@RequstBodyrequired属性是否为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方法

首先获取请求头<code>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 {

// 获取请求头ContentType

contentType = 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方法,筛选每个消息过滤器是否能将请求信息转为指定的媒体类型contentType

if (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<code>筛选方法

// AbstractJackson2HttpMessageConverter类方法

@Override

public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {

// 判断是否支持传入的mediaType

if (!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.readjson反序列化为对象方法

// 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容器启动流程介绍过,类上有<code>@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什么都不传才算,即使{}也不算空)

@Nullable

Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,

Type targetType, Class<? extends HttpMessageConverter<?>> converterType);

}

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

获取所有的请求增强器,调用supports方法

返回true,表示当前增强器满足条件,接下来调用beforeBodyRead方法`对请求信息做处理返回false,表示当前增强器不满足条件,跳过去校验下一个增强器

// 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;

}

在这里插入图片描述

查看<code>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、消息转换器

这里用的消息转换器与获取请求参数里的转换器一样,都是<code>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) {

// 判断是否支持传入的mediaType

if (!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() {

@Override

public OutputStream getBody() {

return outputStream;

}

@Override

public HttpHeaders getHeaders() {

return headers;

}

}));

}

else {

// jackson序列化方法

writeInternal(t, type, outputMessage);

outputMessage.getBody().flush();

}

}

核心方法:对象转换为json字符串并写入输出流

在这里插入图片描述

3.2.2、ResponseBodyAdvice响应增强器

上篇文章SpringMVC源码解析(一):web容器启动流程介绍过,类上有<code>@ControllerAdvice注解且实现ResponseBodyAdvice接口的即为ResponseBodyAdvice增强器,主要就是在返回对象转换响应信息做一些扩展处理。

ResponseBodyAdvice响应增强器

使用场景:对response数据统一封装或者加密等操作

public interface ResponseBodyAdvice<T> {

// 是否支持,自定义判断条件

// 该方法返回true时,才会进去下面的 beforeBodyWrite方法

boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);

// 响应写入之前调用

@Nullable

T 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,则正常遍历所有的拦截器,并调用所有拦截器的<code>preHandle方法返回false,则不再循环遍历后面的拦截器,只会调用当前拦截器的最终方法afterCompletion,并且Handler都不再执行,直接返回

// 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})

@ResponseBody

public ResponseEntity<String> exceptionHandler(Exception e) {

return ResponseEntity.status(500).body("系统异常");

}

}

异常处理器触发位置

在这里插入图片描述

<code>// 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等转换器,也可以通过导包导入jacksonfastjson转换器对于json转换器就是将请求信息里body的json字符串反序列化为java对象在转换对象前后,SpringMVC留下了扩展点,请求增强器RequestponseBodyAdvice,可以对转换前的body和转换后的对象做扩展处理 执行Handler就很简单了,直接method.invoke(obj,args)反射调用方法即可执行Handler后,使用返回值处理器对象返回值做处理了

对于类上或方法上有@ResponseBody的,使用消息转换器将java对象序列化为json字符串(以后会传给前端)同样也是再转换前,SpringMVC留下了扩展点,响应增强器ResponseBodyAdvice,可以对方法返回值做扩展处理再序列化 再说下我们常用的扩展点,拦截器,在方法执行前后最后无论是否抛异常都会执行的三个位置,都可以做扩展处理



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。