1、SpringMVC请求处理流程
SpringMVC的核心组件和请求处理流程如下:
-
1、
DispatcherServlet
是SpringMVC
中的前端控制器(front controller
),负责接收request
并将request
转发给对应的处理组件。 -
2、
HanlerMapping
是SpringMVC
中完成url
到Controller
映射的组件。DispatcherServlet
接收request
,然后从HandlerMapping
查找处理request
的controller
. -
3、
Cntroller
处理request
,并返回ModelAndView
对象,Controller
是SpringMVC
中负责处理request
的组件(类似于struts2
中的Action
),ModelAndView
是封装结果视图的组件。 -
4、视图解析器解析
ModelAndView
对象并返回对应的视图给客户端。
2、DispatcherServlet
前端控制器,也是处理请求开始的地方,先看一下它的结构,然后在逐一分析里面的不同组件。
- 可以看到它是继承了
HttpServlet
的,用来接收请求的。 - 也是实现了
ApplicationContextAware
接口的可以猜到它是用来获取需要的相关组件的。
我们进去它的父类FrameworkServlet
会看到Servlet
在初始化的时候会给Spring
注册一个容器刷新的监听事件,在容器初始化完之后会进来刷新执行初始化操作。
onRefresh
则是从容器中获取需要的相关组件,比如MultipartResolver
、HandlerMappings
、HandlerAdapters
等组件。
处理请求的位置则是在org.springframework.web.servlet.DispatcherServlet#doDispatch
,直接看源码的注释。
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);
multipartRequestParsed = (processedRequest != request);
// 1、根据请求找到对应的控制器执行器链HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2、找到对应控制器的适配器,用来执行操作的
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 执行拦截器前置
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 正在执行控制器逻辑
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 执行拦截器后置
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 处理返回结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
DispatcherServlet
的大概流程和原理就是这样,但是只看这个基本跟没看没区别,所以我们需要去了解里面的主要的几种组件的原理。
本文只分析了HandlerMapping
、HandlerAdapter
两种组件。
3、HandlerMapping组件
这里只针对RequestMappingHandlerMapping
这个处理器映射器解析,它就是处理@Controller
、@RequestMapping
等注解的组件了。先看一下它的组成结构,然后再逐一分析它的每一个接口或者抽象类的功能。
可以看到接口功能类型分两类:
- 第一部分是
Spring
容器的扩展支持 - 第二部分就是
HandlerMapping
的功能实现了。
第一部分就是在设置SpringContext
的时候执行初始化的功能,从源码可以清楚的看到就是扩展、初始化、判断包装了一下拦截器而已。
重点还是在第二部分,我会根据接口的继承层次一个一个分别讲解。
1、先看HandlerMapping
接口,在这个接口中只定义了一个规范,就是接受一个请求返回一个控制器的执行链。
public interface HandlerMapping {
....略
/**
根据请求返回一个HandlerExecutionChain 控制器质性链
**/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
在HandlerExecutionChain
里面包含了handler
、拦截器
等主要属性。
其中handler
在不同的处理器映射器中存的对象不同,就是具体的执行对象了。在RequestMappingHandlerMapping
中存的就是HandlerMethod
拦截器的功能则是在applyPreHandle
、applyPostHandle
、triggerAfterCompletion
、applyAfterConcurrentHandlingStarted
这些方法中执行。
2、接着在基础抽象类AbstractHandlerMapping
看一下getHandler
的处理逻辑。
这一步会有子类去getHandlerInternal
获取handler
,如果是String
的话会当做BeanName
去容器中获取具体的对象。
@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;
}
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 拿到handler之后就去获取HandlerExecutionChain
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
// 如果有cors则去获取
if (hasCorsConfigurationSource(handler)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
// 最后返回
return executionChain;
}
其中getHandlerExecutionChain
根据url
找出匹配的拦截器放进去,然后再返回。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 获取url
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
// 从已经有的拦截器判断是否MappedInterceptor,如果是则需要匹配路径,不是则默认拦截
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
至于getHandlerInternal
方法由子类实现,先不讲,后面讲到的了继续分析。
3、接着看AbstractHandlerMethodMapping<T>
抽象类:
在属性设置完之后会去初始化handlerMethods
在processCandidateBean
方法中会判断如果类有@Controller
、@RequestMapping
直接就会去detectHandlerMethods
。
protected void detectHandlerMethods(Object handler) {
// 如果是String当做类型去容器中获取
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 循环类的每一个方法,获取方法对应Mapping信息
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
// 然后注册进mappingRegistry中
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
在detectHandlerMethods
中的逻辑主要就是:
- 获取每个方法对象
MappingInfo
信息,其中getMappingForMethod(method, userType)
是由子类去实现的。 registerHandlerMethod
则是将Method
、handler
、mapping
注册到MappingRegistry
中。
需要注意的是这一步的handler
可以是beanName
也可以是具体的Bean
(也就是Controller
)。
4、接着看RequestMappingInfoHandlerMapping
,这个抽象类主要是做RequestMappingInfo
的匹配判断比较逻辑的。
主要需要了解的是RequestMappingInfo的组成。
如果你看了@RequestMapping
注解的话,你就会发现这里就是存了url
的匹配条件。
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] consumes() default {};
String[] produces() default {};
}
上面已经扫描的Bean
然后解析了MappingInfo
信息,还有一个接口就是getHandlerInternal
从已经注册的缓存中获取HandlerMethod
。
5、最后看RequestMappingHandlerMapping
这里比较核心的方法有:
-
isHandler
:判断handler
类型
-
createRequestMappingInfo
:解析注解构建RequestMappingInfo
组成大概就这些,我们直接debug
进去看下最后RequestMappingHandlerMapping
最终获取的HandlerExecutionChain
的组成:
- 拦截器集合
HandlerMethod
:包括控制层对象、这个请求匹配的方法、方法参数等信息。
总的来说,RequestMapping
就是解析url
并注册记录,然后负责根据请求匹配到某个具体的执行对象。
4、HandlerAdapter组件
获取到了对应的handler
之后就是执行这个handler
了,就交给了HandlerAdapter
适配器来执行,并且处理返回结果。
先看一下它的接口定义了哪些规范。
public interface HandlerAdapter {
// 判断支持的handler
boolean supports(Object handler);
// 执行handler并且返回结果。
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
// 根据handler决定是否需要修改request
long getLastModified(HttpServletRequest request, Object handler);
}
第一个的话就是判断支持对象了:
具体的执行方法:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
突然又回来看看了。补充下。
ServletInvocableHandlerMethod
这个对象就是handlerMethod
的子类,内含handlerMethod
并且进行扩展。
接着看:invocableMethod.invokeAndHandle(webRequest, mavContainer);
的源码逻辑:
再往下看:
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
这一步就是用参数解析器和convert
将请求参数解析成具体的值,源码如下:
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
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;
}
// 1、这里根据类的对象、参数、包含什么注解啊,只要有一类解析器支持就通过了。
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 这一步和1是一样的获取到解析器,然后去解析
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
以RequestResponseBodyMethodProcessor
为例,他是requestBody
请求注解解析的解析器:
追的它的这个方法内:readWithMessageConverters
,就是用messageConverters
匹配到第一个支持的convert把request中的内容读取出来装换成对象,比如AbstractJackson2HttpMessageConverter
,就是使用objectMapper
反序列化为对象的。
像什么LocalDateTime
之类的对象,如果希望统一入参和返回的格式什么的,都可以对messageConverters
进行扩展实现。