缘起
接上一篇文章Servlet的前世今生,继续我的好奇之旅。这一篇简单的介绍一下Spring MVC是如何工作的。
先放一张图为敬
依然从Servlet讲起
这次不是简单的Servlet了,是DispatcherServlet。至于为什么直接从DispatcherServlet开始,请看文首的那篇文章。
1.浏览器发起请求,请求来到DispatcherServlet;
关键代码:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
doDispatch(request, response);
// ...
}
2.DispatcherServlet找到处理这个请求的处理器mappedHandler;
关键代码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
// ...
}
3.根据找到的mappedHandler获取它的适配器HandlerAdapter;
关键代码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ...
}
4.使用HandlerAdapter来处理请求,本质上还是使用Handler处理的请求,返回一个ModelAndView对象mv给DispatcherServlet;
注:此处讲一下Spring MVC解析请求参数的细节。
关键代码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// ...
}
从此处可以看出本质上还是使用的handler处理的请求
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// ...
mav = invokeHandlerMethod(request, response, handlerMethod);
// ...
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// ...
invocableMethod.invokeAndHandle(webRequest, mavContainer);
// ...
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// ...
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
从下面这段代码可以看出,要调用我们自己写的Controller中的方法,最终还是使用的反射。
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);
}
那么,我们的入参有各种各样的类型和形式,例如有使用@RequestParam,有@PathVariable,也有@RequestBody,Spring MVC是怎样统一处理不同类型的入参的,关键代码就在上面这个方法的第一行中。下面是这个方法:
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;
}
// 此处获取参数解析器
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) {
// 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;
}
从上面的代码中可以看出,获取请求的每个参数,根据他使用的注解找到对应的参数解析器,例如RequestParamMethodArgumentResolver,对参数进行解析。最后放在一个数组里统一返回,使用反射进行方法调用。
5.DispatcherServlet调用ViewResolver将mv解析成View对象;
注:此处讲一下Spring MVC解析视图的细节。
入口在DispatcherServlet中,如下面代码中的render方法:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
// ...
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
// ...
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// ...
// We need to resolve the view name.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
// ...
view.render(mv.getModelInternal(), request, response);
}
下面这段代码是找到合适的ViewResolver,将ModelAndView转成View。假设使用的是Thymeleaf,如果返回的是Thymeleaf页面,View将由ThymeleafViewResolver来解析。
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}