Running with Spring Boot v2.5.4, Java 11.0.12
Spring MVC
是一款构建于Servlet API
之上、基于同步阻塞I/O
模型的主流Java Web开发框架,这种I/O模型意味着一个Http请求对应一个线程,即每一个Http请求都是在各自线程上下文中完成处理的;此外,Spring 5.0提供了一款基于异步非阻塞I/O
模型的Java Web开发框架,即Spring WebFlux
;大家不用纠结Spring官方会不会在将来的某个时间点将Spring MVC置为废弃(deprecated)态,至少目前来看,Spring MVC依然是流行的,在Spring官网关于Reactive
的介绍中有一张图相当精致,与大家分享:
笔者自2017年7月毕业后一直服役于某央企一子公司,第一年主要参与集团公司委派的课题性项目,基本没写啥代码;自18年国庆至今,虽然辗转于多个项目组,但角色一直没变,那就是CRUD BOY,还特么挺稳的,哈哈。即使菜如CRUD BOY,也没有理由不掌握Spring MVC的相关知识,为什么这么忽悠呢?以Tomcat
为例,它是目前应用最为广泛的Servlet容器
,当Tomcat接收到一个Http请求后,底层复杂的Socket解析工作由它代劳了,Http请求解析完成后,它直接将HttpServletRequest
与HttpServletResponse
(这时候还是一个空的对象)对象一并传给Servlet
处理,大家只需要面向Servlet编程即可;在Spring MVC框架问世后,Servlet开始退居幕后,Java程序猿的工作变得更加轻松而聚焦了,压根不再需要和HttpServletRequest、HttpServletResponse打交道,会复制粘贴@RestController
就行;前辈们做了这么多苦活、累活,如果连Spring MVC内部处理流程还一无所知,实在有点说不过去了···
1. 写在前面
一个Http请求在Spring应用中的执行链路比较长,涉及逻辑也比较多,下面通过阿里的Arthas
工具来探测这条执行链路。
1.1 编写Controller并启动应用
代码语言:javascript复制@RestController
@RequestMapping(path = "/crimson_typhoon")
public class CrimsonTyphoonController {
@PostMapping(path = "/v1/fire")
public Map<String, Object> v1Fire(@RequestBody @Valid UserDto userDto,
@RequestParam("dryRun") Boolean dryRun) {
return ImmutableMap.of("status", "success", "code", 200);
}
}
@Getter
@Setter
@NoArgsConstructor
public class UserDto {
@NotBlank
private String name;
@NotNull
private int age;
}
1.2 启动Arthas
执行如下命令以监听并在控制台打印CrimsonTyphoonController
中v1Fire()
方法的执行路径。
stack com.example.crimson_typhoon.controller.CrimsonTyphoonController v1Fire
1.3 发起Http请求
代码语言:javascript复制curl --location --request POST 'http://localhost:8080/crimson_typhoon/v1/fire?dryRun=true'
--header 'Content-Type: application/json'
--data-raw '{
"name": "crimson_typhoon",
"age": 18
}'
1.4 执行链路
代码语言:javascript复制如果有大佬是从上往下看这串执行链路信息的,这边建议您出门左转,谢谢!
ts=2021-11-09 21:22:37;thread_name=http-nio-8080-exec-2;id=37;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@149debbb
@com.example.crimson_typhoon.controller.CrimsonTyphoonController.v1Fire()
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1064)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1726)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:834)
大家应该对上面这坨信息很熟悉才对,因为在排查问题时经常可以在日志中看到;上述执行链路交代了两件事:
- 当一个Http请求到达后,Tomcat从其线程池中捞出一个线程来处理该Http请求的,记得《Go语言学习笔记》这本书中曾经提到:一切Go程序都是基于Channel的,Java又何尝不是呢?一切Java程序都是基于Thread的;
- 在Http请求进入Spring MVC之前,先要依次过一遍
ApplicationFilterChain
中的Filter
,默认有4个:OrderedCharacterEncodingFilter
、OrderedFormContentFilter
、OrderedRequestContextFilter
和WsFilter
。
Filter来源于Servlet规范
,并不是Spring中的术语。跟Filter紧密联系的还有FilterConfig
和FilterChain
,它们的主要内容如下图所示:
Filter既可以过滤HttpServletRequest,也可以过滤HttpServletResponse;FilterConfig由Servlet容器组装,以初始化Filter;FilterChain同样由Servlet容器组装,为开发人员提供Filter链的执行视图,FilterChain是责任链模式的一个典型应用;在Filter链的结尾,将会调用真正的资源,比如在ApplicationFilterChain中,Filter链执行完毕后,将Http请求委派给DispatcherServlet
处理,而DispatcherServlet恰恰就是Spring MVC的门户,如下所示:
public final class ApplicationFilterChain implements FilterChain {
@Override
public void doFilter(ServletRequest request, ServletResponse response) {
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos ];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
return;
}
// 过滤器链中的所有过滤器执行完毕后,
// 将HTTP请求委派给DispatcherServlet处理;
servlet.service(request, response);
}
}
关于Filter还有一个比较重要的知识点,那就是Filter的执行流程,类似一种U型链路,如下图所示:
2. Spring MVC处理流程
如果单单看Arthas针对v1Fire()
方法输出的执行路径,会造成大家误认为Spring MVC处理流程很简短的假象,事实上,当我们一步一步DEBUG的时候,才知道这特么完全是无底洞啊!刚刚提到DispatcherServlet是Spring MVC的门户,那自然要从它开始了,在介绍DispatcherServlet之前,先来看看它的继承关系:
在Front Controller
设计模式中,通常由一个核心Controller负责将Http请求路由到其他Controller中处理,Spring MVC实现了这一模式,这个核心Controller就是DispatcherServlet。DispatcherServlet就像一个包工头,只揽活却不干活,当接收到Http请求后,它会将该请求委派给HandlerMapping
、HandlerAdapter
、HandlerExceptionResolver
和ViewResolver
等小弟处理。doDispatch()
方法是其核心逻辑所在,为了更直观地展现Spring MVC整体流程骨架,笔者剔除了一些不相干的逻辑,如下所示:
public class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerExecutionChain mappedHandler = null;
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// Determine handler for the current request.
mappedHandler = getHandler(request);
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Invoke all HandlerInterceptor preHandle() in HandlerExecutionChain.
if (!mappedHandler.applyPreHandle(request, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(request, response, mappedHandler.getHandler());
// Invoke all HandlerInterceptor postHandle() in HandlerExecutionChain.
mappedHandler.applyPostHandle(request, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
// Handle the result of handler invocation, which is either a ModelAndView
// or an Exception to be resolved to a ModelAndView.
processDispatchResult(request, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
// Invoke all HandlerInterceptor afterCompletion() in HandlerExecutionChain.
triggerAfterCompletion(request, response, mappedHandler, ex);
}
}
}
Spring MVC的内部逻辑与HandlerMapping、HandlerAdapter息息相关,下面将从两个章节对它们进行分析。
2.1 HandlerMapping
HandlerMapping
用于将Http请求映射到对应的HandlerExecutionChain
:
public interface HandlerMapping {
/**
* @param request current HTTP request
* @return a HandlerExecutionChain instance containing handler object and
* any interceptors
*/
HandlerExecutionChain getHandler(HttpServletRequest request);
}
而HandlerExecutionChain
内部维护了handler
和interceptorList
这俩个成员变量。其中,handler一般指的是由@Controller
标注的控制器,interceptorList指的是HandlerInterceptor
列表。HandlerExecutionChain的精简版源码如下:
public class HandlerExecutionChain {
private final Object handler;
private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
public HandlerExecutionChain(Object handler, List<HandlerInterceptor> interceptorList) {}
// Apply preHandle methods of registered interceptors.
public boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) {}
// Apply postHandle methods of registered interceptors.
public void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) {}
// Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
public void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) {}
}
紧接着,我们再来看一下HandlerInterceptor
。顾名思义,这个拦截器可以在handler执行前后进行拦截操作,具体通过下面三个方法:
public interface HandlerInterceptor {
// Interception point before the execution of a handler.
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
return true;
}
// Interception point after successful execution of a handler.
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
}
// Callback after completion of request processing.
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
}
}
HandlerInterceptor的执行流程也很重要,尤其是postHandle()
与afterCompletion()
这俩方法,具体参见下图:
那HandlerMapping需要开发人员自定义吗?一般是不需要这么做的。默认地,Spring MVC会提供一些不同映射规则的HandlerMapping,这些HandlerMapping会通过initHandlerMappings()
方法提前填充到DispatcherServlet中的handlerMappings
成员变量中:
private void initHandlerMappings(ApplicationContext context) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
handlerMappings中各元素如下:
代码语言:javascript复制0 = RequestMappingHandlerMapping
1 = BeanNameUrlHandlerMapping
2 = RouterFunctionMapping
3 = SimpleUrlHandlerMapping
4 = WelcomePageHandlerMapping
RequestMappingHandlerMapping
无疑是最重要的一个HandlerMapping,从其名称大概能猜测到其映射规则一定与@RequestMapping
注解有关,刚好CrimsonTyphoonController也由@RequestMapping标注。下面就来聊聊它是如何根据Http请求获取到相匹配的HandlerExecutionChain的。抽象类AbstractHandlerMethodMapping
是RequestMappingHandlerMapping的父类,它实现了InitializingBean
接口,那应该会覆盖afterPropertiesSet()
方法,否则不是闲得无聊吗?从其源码来看果然如此:
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
initHandlerMethods()
方法主要用于初始化AbstractHandlerMethodMapping中MappingRegistry
类型的成员变量。具体地,通过遍历所有Bean,判断当前Bean是否由@Controller
或@RequestMapping
注解标注;若是,则填充MappingRegistry类中HashMap<RequestMappingInfo, MappingRegistration>
类型的成员变量,即registry;HashMap中key/value信息如下:
key = RequestMappingInfo{POST /crimson_typhoon/v1/fire}
value = MappingRegistration{
HandlerMethod{
public java.util.Map com.example.crimson_typhoon.controller.CrimsonTyphoonController.v1Fire(
com.example.crimson_typhoon.dto.UserDto,
java.lang.Boolean
)
}
}
有了上面这层映射关系,根据Http请求获取对应的HandlerMethod就是轻而易举的事了(在HandlerMethod中,有两个比较重要的成员变量,分别是Object
类型的bean
变量和java.lang.reflect.Method
类型的method
变量);最后,将HandlerMethod与HandlerInterceptor封装到HandlerExecutionChain实例中去就结束了。
handler其实是被封装在HandlerMethod实例中的。
2.2 HandlerAdapter
上一小节主要介绍如何根据Http请求获取相匹配的HandlerExecutionChain实例;在本小节中,将重点关注HandlerExecutionChain中HandlerMethod的执行流程。既然HandlerMethod中的method变量是java.lang.reflect.Method
类型的,那这架势肯定是奔着反射去的了,回顾下java.lang.reflect.Method
的核心API:
public class Method {
public Object invoke(Object obj, Object... args) {
// 实现细节忽略
}
}
很显然,method实例要想顺利调用其invoke()
方法,现在还差一个方法参数,那就从参数解析开始分析吧。
顾名思义,HandlerAdapter
是一个适配器,它为Spring MVC带来了拓展性,可以适配任意类型的handler,绝不仅仅是那些由@Controller
注解标注的handler。与HandlerMapping类似,开发人员若无特殊需求,一般也无需自定义HandlerAdapter。默认地,Spring MVC会提供一些可以适配不同handler的HandlerAdapter,比如:适配org.springframework.web.servlet.mvc.Controller
接口的SimpleControllerHandlerAdapter
,适配javax.servlet.Servlet
接口的SimpleServletHandlerAdapter
等;此外,这些HandlerAdapter是通过initHandlerAdapters()
方法提前填充到DispatcherServlet中这一handlerAdapters
成员变量中的:
private void initHandlerAdapters(ApplicationContext context) {
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
handlerAdapters中各元素如下:
代码语言:javascript复制0 = RequestMappingHandlerAdapter
1 = HandlerFunctionAdapter
2 = HttpRequestHandlerAdapter
3 = SimpleControllerHandlerAdapter
RequestMappingHandlerAdapter
既然能排在第一把交椅,那足以说明它的重要性,它主要用于适配HandlerMethod类型的handler,如下:
public class RequestMappingHandlerAdapter {
/**
* This implementation expects the handler to be an HandlerMethod.
*/
@Override
public final boolean supports(Object handler) {
return handler instanceof HandlerMethod;
}
}
DispatcherServlet发现RequestMappingHandlerAdapter的supports()
方法返回true
,就不再继续寻找适配器了,因为最匹配的适配器已经找到。
此外,RequestMappingHandlerAdapter同样实现了InitializingBean
接口,用于初始化argumentResolvers
与returnValueHandlers
这俩成员变量,如下所示:
@Override
public void afterPropertiesSet() {
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
argumentResolvers中各元素如下:
代码语言:javascript复制 0 = RequestParamMethodArgumentResolver
1 = RequestParamMapMethodArgumentResolver
2 = PathVariableMethodArgumentResolver
3 = PathVariableMapMethodArgumentResolver
4 = MatrixVariableMethodArgumentResolver
5 = MatrixVariableMapMethodArgumentResolver
6 = ServletModelAttributeMethodProcessor
7 = RequestResponseBodyMethodProcessor
8 = RequestPartMethodArgumentResolver
9 = RequestHeaderMethodArgumentResolver
10 = RequestHeaderMapMethodArgumentResolver
11 = ServletCookieValueMethodArgumentResolver
12 = ExpressionValueMethodArgumentResolver
13 = SessionAttributeMethodArgumentResolver
14 = RequestAttributeMethodArgumentResolver
15 = ServletRequestMethodArgumentResolver
16 = ServletResponseMethodArgumentResolver
17 = HttpEntityMethodProcessor
18 = RedirectAttributesMethodArgumentResolver
19 = ModelMethodProcessor
20 = MapMethodProcessor
21 = ErrorsMethodArgumentResolver
22 = SessionStatusMethodArgumentResolver
23 = UriComponentsBuilderMethodArgumentResolver
24 = PrincipalMethodArgumentResolver
25 = RequestParamMethodArgumentResolver
26 = ServletModelAttributeMethodProcessor
argumentResolvers
是HandlerMethodArgumentResolverComposite
类型的,后者是一个复合类,持有多个HandlerMethodArgumentResolver
类型的方法参数解析器。当需要获取方法参数解析器时,HandlerMethodArgumentResolverComposite会遍历其所持有的所有参数解析器,若HandlerMethodArgumentResolver的supportsParameter()
方法返回true
,这意味着找到了最匹配的方法参数解析器,不再继续查找。在本文中,Http请求携带了两种参数,具体如下:
------------------------------
{
"name": "crimson_typhoon",
"age": 28
}
------------------------------
dryRun=true
------------------------------
对于Http请求体中的JSON串,交由argumentResolvers中的RequestResponseBodyMethodProcessor
负责解析;它有两个作用,一是将Http请求体中的JSON串封装到由@RequestBody
标注的参数实例中,即UserDto
,二是将由@ResponseBody
标注的handler中方法的返回值写入到Http响应体中,参数解析相关源码如下:
public class RequestResponseBodyMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
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) {
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
}
}
}
return adaptArgumentIfNecessary(arg, parameter);
}
}
RequestResponseBodyMethodProcessor持有的messageConverters
成员变量中内嵌了近10个HttpMessageConverter
类型的转换器,因此具体的参数解析工作将由其中一个转换器负责,至于究竟是哪一个转换器,这要看canRead()
方法是否能返回true
了,若返回值为true
,则会通过该转换器的read()
方法解析出最终的参数;本文所提及的JSON串最终是由MappingJackson2CborHttpMessageConverter
负责填充到UserDto
实例中去的(由于@Valid
注解的存在,还会紧接着进行Bean Validation
操作)。此外,HttpMessageConverter具备双向转换能力,其源码如下:
public interface HttpMessageConverter<T> {
// Indicates whether the given class can be read by this converter.
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
// Indicates whether the given class can be written by this converter.
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
// Read an object of the given type from the given input message, and returns it.
T read(Class<? extends T> clazz, HttpInputMessage inputMessage);
// Write an given object to the given output message.
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
}
对于Http请求URL中的参数,交由argumentResolvers中的RequestParamMethodArgumentResolver
负责,具体借助org.springframework.core.convert.support.Converter
接口的实现类StringToBooleanConverter
将字符串类型值转换为布尔值。
argumentResolvers中各元素如下:
代码语言:javascript复制 0 = ModelAndViewMethodReturnValueHandler
1 = ModelMethodProcessor
2 = ViewMethodReturnValueHandler
3 = ResponseBodyEmitterReturnValueHandler
4 = StreamingResponseBodyReturnValueHandler
5 = HttpEntityMethodProcessor
6 = HttpHeadersReturnValueHandler
7 = CallableMethodReturnValueHandler
8 = DeferredResultMethodReturnValueHandler
9 = AsyncTaskMethodReturnValueHandler
10 = ServletModelAttributeMethodProcessor
11 = RequestResponseBodyMethodProcessor
12 = ViewNameMethodReturnValueHandler
13 = MapMethodProcessor
14 = ServletModelAttributeMethodProcessor
returnValueHandlers
是HandlerMethodReturnValueHandlerComposite
类型的,后者也是一个复合类,持有多个HandlerMethodReturnValueHandler
类型的方法返回值解析器。由于CrimsonTyphoonController由@RestController
标注,而@RestController注解接口又由@ResponseBody
标注,因此RequestResponseBodyMethodProcessor
最终从这些候选解析器中脱颖而出,很熟悉对不对?废话不多说,这一解析器依然会委派MappingJackson2CborHttpMessageConverter
将v1Fire()方法的返回值转换为JSON串,然后写入到Http响应体中去。
public class RequestResponseBodyMethodProcessor {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
}
目前,前后端分离已成为业界开发与部署的标准模式,压根不会将html、jsp等页面嵌入在后端应用中,因此视图渲染就不再赘述了。
3. 总结
事实上,本文也只是粗略分析了Spring MVC的处理流程,还有一些重要的细节没有覆盖,比如:统一异常处理,限于篇幅,后续再介绍它的原理与最佳实践方案吧。
参考文档
- https://spring.io/reactive
- https://docs.spring.io/spring-framework/docs/current/reference/html/