Spring MVC如何工作?

缘起

  接上一篇文章Servlet的前世今生,继续我的好奇之旅。这一篇简单的介绍一下Spring MVC是如何工作的。

先放一张图为敬

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;
}
6.DispatcherServlet将View对象渲染返回给浏览器,展示在页面上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值