面试官:小伙汁,你画的SpringMVC请求处理过程是从网上抄的吧?

拼搏现实的明天。 2022-11-22 05:25 110阅读 0赞

点击上方“java大数据修炼之道”,选择“设为星标”

优质文章和精品资源, 第一时间送达format_png

关注订阅号「Java大数据修炼之道」,收看更多精彩内容

来自:Sicimike

链接:blog.csdn.net/Baisitao_/article/details/107471719

往日回顾:面试官:来说说单点登录的三种实现方式


正文

前言

SpringMVC请求处理相信大家都很熟悉了,本篇主要是基于SpringMVC处理请求的流程来阅读并调试源码,以及解决几个仅靠流程图无法解释的问题。

本篇使用的Spring版本为5.2.2.RELEASE

九大组件

SpringMVC几乎所有的功能都由九大组件来完成,所以明白九大组件的作用,对于学习SpringMVC来说非常重要。

  1. /** 文件上传解析器 */
  2. private MultipartResolver multipartResolver;
  3. /** 区域解析器,用于国际化 */
  4. private LocaleResolver localeResolver;
  5. /** 主题解析器 */
  6. private ThemeResolver themeResolver;
  7. /** Handler映射信息 */
  8. private List<HandlerMapping> handlerMappings;
  9. /** Handler适配器*/
  10. private List<HandlerAdapter> handlerAdapters;
  11. /** Handler执行异常解析器 */
  12. private List<HandlerExceptionResolver> handlerExceptionResolvers;
  13. /** 请求到视图的转换器 */
  14. private RequestToViewNameTranslator viewNameTranslator;
  15. /** SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap */
  16. private FlashMapManager flashMapManager;
  17. /** 视图解析器 */
  18. private List<ViewResolver> viewResolvers;
  • HandlerMappingHandler映射信息,根据请求携带的url信息查找处理器(Handler)。每个请求都需要对应的Handler处理。
  • HandlerAdapterHandler适配器,SpringMVC没有直接调用处理器(Handler),而是通过HandlerAdapter来调用,主要是为了统一Handler的调用方式
  • ViewResolver:视图解析器,用来将字符串类型的视图名称解析为View类型的视图。ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。
  • MultipartResolver:文件上传解析器,主要用来处理文件上传请求
  • HandlerExceptionResolver:Handler执行异常解析器,用来对异常进行统一处理
  • RequestToViewNameTranslator:请求到视图的转换器
  • LocaleResolver:区域解析器,用于支持国际化
  • FlashMapManagerSpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap
  • ThemeResolver:主题解析器,用于支持不同的主题

九大组件中最重的的前三个,HandlerMappingHandlerAdapterViewResolver,因为这是阅读源码时,避不开的三个组件。

调试准备

搭建一个基本的Spring web项目即可

Controller部分

  1. @Controller
  2. public class IndexController {
  3. @RequestMapping("/index/home")
  4. public String home(String id, Student student, @RequestParam("code") String code) {
  5. System.out.println(student.getName());
  6. return "index";
  7. }
  8. @ResponseBody
  9. @RequestMapping("/index/list")
  10. public String list() {
  11. return "success";
  12. }
  13. }

Entity部分

  1. public class Student {
  2. private String name;
  3. private Integer gender;
  4. // getter、setter
  5. }

还是那句话,Spring源码非常庞大,不能只见树木不见森林,需要有针对性的阅读,所以本篇只需要关注主体流程即可。

核心方法

我们都知道,SpringMVC有一个用来分发请求的前端控制器DispatcherServlet,其中用来处理请求的方法就是doService,该方法定义如下

doService

  1. /**
  2. * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
  3. * for the actual dispatching.
  4. */
  5. @Override
  6. protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  7. logRequest(request);
  8. // Keep a snapshot of the request attributes in case of an include,
  9. // to be able to restore the original attributes after the include.
  10. Map<String, Object> attributesSnapshot = null;
  11. if (WebUtils.isIncludeRequest(request)) {
  12. attributesSnapshot = new HashMap<>();
  13. Enumeration<?> attrNames = request.getAttributeNames();
  14. while (attrNames.hasMoreElements()) {
  15. String attrName = (String) attrNames.nextElement();
  16. if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
  17. attributesSnapshot.put(attrName, request.getAttribute(attrName));
  18. }
  19. }
  20. }
  21. // Make framework objects available to handlers and view objects.
  22. request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  23. request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  24. request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  25. request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
  26. if (this.flashMapManager != null) {
  27. FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  28. if (inputFlashMap != null) {
  29. request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
  30. }
  31. request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  32. request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
  33. }
  34. try {
  35. // 真正执行的方法
  36. doDispatch(request, response);
  37. }
  38. finally {
  39. if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  40. // Restore the original attribute snapshot, in case of an include.
  41. if (attributesSnapshot != null) {
  42. restoreAttributesAfterInclude(request, attributesSnapshot);
  43. }
  44. }
  45. }
  46. }

doDispatch

doDispatchdoService中真正用来处理请求的方法

  1. /**
  2. * 实际处理请求的方法
  3. */
  4. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  5. HttpServletRequest processedRequest = request;
  6. HandlerExecutionChain mappedHandler = null;
  7. boolean multipartRequestParsed = false;
  8. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  9. try {
  10. ModelAndView mv = null;
  11. Exception dispatchException = null;
  12. try {
  13. // 校验是否是文件上传请求
  14. processedRequest = checkMultipart(request);
  15. multipartRequestParsed = (processedRequest != request);
  16. // Determine handler for the current request.
  17. // 为当前请求找到一个合适的处理器(Handler)
  18. // 返回值是一个HandlerExecutionChain,也就是处理器执行链
  19. mappedHandler = getHandler(processedRequest);
  20. if (mappedHandler == null) {
  21. noHandlerFound(processedRequest, response);
  22. return;
  23. }
  24. // Determine handler adapter for the current request.
  25. // 根据HandlerExecutionChain携带的Handler找到合适的HandlerAdapter
  26. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  27. // Process last-modified header, if supported by the handler.
  28. // 处理GET请求的缓存
  29. String method = request.getMethod();
  30. boolean isGet = "GET".equals(method);
  31. if (isGet || "HEAD".equals(method)) {
  32. long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
  33. if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
  34. return;
  35. }
  36. }
  37. // 执行拦截器的preHandle方法
  38. if (!mappedHandler.applyPreHandle(processedRequest, response)) {
  39. return;
  40. }
  41. // Actually invoke the handler.
  42. // 利用HandlerAdapter来执行Handler里对应的处理方法
  43. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  44. if (asyncManager.isConcurrentHandlingStarted()) {
  45. return;
  46. }
  47. // 如果没有设置视图,则应用默认的视图名
  48. applyDefaultViewName(processedRequest, mv);
  49. // 执行拦截器的postHandle方法
  50. mappedHandler.applyPostHandle(processedRequest, response, mv);
  51. }
  52. catch (Exception ex) {
  53. dispatchException = ex;
  54. }
  55. catch (Throwable err) {
  56. // As of 4.3, we're processing Errors thrown from handler methods as well,
  57. // making them available for @ExceptionHandler methods and other scenarios.
  58. dispatchException = new NestedServletException("Handler dispatch failed", err);
  59. }
  60. // 根据ModelAndView对象解析视图
  61. processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  62. }
  63. catch (Exception ex) {
  64. triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  65. }
  66. catch (Throwable err) {
  67. triggerAfterCompletion(processedRequest, response, mappedHandler,
  68. new NestedServletException("Handler processing failed", err));
  69. }
  70. finally {
  71. if (asyncManager.isConcurrentHandlingStarted()) {
  72. // Instead of postHandle and afterCompletion
  73. if (mappedHandler != null) {
  74. mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
  75. }
  76. }
  77. else {
  78. // Clean up any resources used by a multipart request.
  79. if (multipartRequestParsed) {
  80. cleanupMultipart(processedRequest);
  81. }
  82. }
  83. }
  84. }

该方法就是SpringMVC处理请求的整体流程,其中涉及到几个重要的方法。

getHandler

该方法定义如下

  1. /**
  2. * Return the HandlerExecutionChain for this request.
  3. * 为这个request返回一个HandlerExecutionChain
  4. */
  5. @Nullable
  6. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  7. if (this.handlerMappings != null) {
  8. for (HandlerMapping mapping : this.handlerMappings) {
  9. HandlerExecutionChain handler = mapping.getHandler(request);
  10. if (handler != null) {
  11. return handler;
  12. }
  13. }
  14. }
  15. return null;
  16. }

调试信息如下

format_png 1

根据调试信息可以看出,getHandler方法主要是从List<HandlerMapping> handlerMappings集合中遍历查找一个合适的处理器(Handler),返回的结果是一个HandlerExecutionChain。然后再根据HandlerExecutionChain里携带的Handler去获取HandlerAdapter

搜索公众号程序员小乐回复关键字“offer”,获取算法面试题和答案。

getHandlerAdapter

getHandlerAdapter方法定义如下

  1. /**
  2. * Return the HandlerAdapter for this handler object.
  3. * @param handler the handler object to find an adapter for
  4. * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
  5. */
  6. protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  7. if (this.handlerAdapters != null) {
  8. for (HandlerAdapter adapter : this.handlerAdapters) {
  9. if (adapter.supports(handler)) {
  10. return adapter;
  11. }
  12. }
  13. }
  14. throw new ServletException("No adapter for handler [" + handler +
  15. "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
  16. }

调试信息如下

format_png 2

同样getHandlerAdapter方法主要是从List<HandlerAdapter> handlerAdapters集合中遍历查找一个合适的处理器适配器(HandlerAdapter),返回的结果是一个HandlerAdapter

可以看到此处HandlerAdapter真正的实现类是RequestMappingHandlerAdapter

processDispatchResult

processDispatchResult方法主要根据方法执行完成后封装的ModelAndView,转发到对应页面,定义如下

  1. /**
  2. * Handle the result of handler selection and handler invocation, which is
  3. * either a ModelAndView or an Exception to be resolved to a ModelAndView.
  4. */
  5. private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
  6. @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
  7. @Nullable Exception exception) throws Exception {
  8. boolean errorView = false;
  9. if (exception != null) {
  10. if (exception instanceof ModelAndViewDefiningException) {
  11. logger.debug("ModelAndViewDefiningException encountered", exception);
  12. mv = ((ModelAndViewDefiningException) exception).getModelAndView();
  13. }
  14. else {
  15. Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
  16. mv = processHandlerException(request, response, handler, exception);
  17. errorView = (mv != null);
  18. }
  19. }
  20. // Did the handler return a view to render?
  21. if (mv != null && !mv.wasCleared()) {
  22. // 主要调用该方法渲染视图
  23. render(mv, request, response);
  24. if (errorView) {
  25. WebUtils.clearErrorRequestAttributes(request);
  26. }
  27. }
  28. else {
  29. if (logger.isTraceEnabled()) {
  30. logger.trace("No view rendering, null ModelAndView returned.");
  31. }
  32. }
  33. if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  34. // Concurrent handling started during a forward
  35. return;
  36. }
  37. if (mappedHandler != null) {
  38. // Exception (if any) is already handled..
  39. mappedHandler.triggerAfterCompletion(request, response, null);
  40. }
  41. }

render

render方法定义如下

  1. /**
  2. * Render the given ModelAndView.
  3. * <p>This is the last stage in handling a request. It may involve resolving the view by name.
  4. * @param mv the ModelAndView to render
  5. * @param request current HTTP servlet request
  6. * @param response current HTTP servlet response
  7. * @throws ServletException if view is missing or cannot be resolved
  8. * @throws Exception if there's a problem rendering the view
  9. */
  10. protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
  11. // Determine locale for request and apply it to the response.
  12. Locale locale =
  13. (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
  14. response.setLocale(locale);
  15. View view;
  16. String viewName = mv.getViewName();
  17. if (viewName != null) {
  18. // We need to resolve the view name.
  19. // 根据给定的视图名称,解析获取View对象
  20. view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
  21. if (view == null) {
  22. throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
  23. "' in servlet with name '" + getServletName() + "'");
  24. }
  25. }
  26. else {
  27. // No need to lookup: the ModelAndView object contains the actual View object.
  28. view = mv.getView();
  29. if (view == null) {
  30. throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
  31. "View object in servlet with name '" + getServletName() + "'");
  32. }
  33. }
  34. // Delegate to the View object for rendering.
  35. if (logger.isTraceEnabled()) {
  36. logger.trace("Rendering view [" + view + "] ");
  37. }
  38. try {
  39. if (mv.getStatus() != null) {
  40. response.setStatus(mv.getStatus().value());
  41. }
  42. view.render(mv.getModelInternal(), request, response);
  43. }
  44. catch (Exception ex) {
  45. if (logger.isDebugEnabled()) {
  46. logger.debug("Error rendering view [" + view + "]", ex);
  47. }
  48. throw ex;
  49. }
  50. }

resolveViewName

resolveViewName方法定义如下

  1. @Nullable
  2. protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
  3. Locale locale, HttpServletRequest request) throws Exception {
  4. if (this.viewResolvers != null) {
  5. for (ViewResolver viewResolver : this.viewResolvers) {
  6. View view = viewResolver.resolveViewName(viewName, locale);
  7. if (view != null) {
  8. return view;
  9. }
  10. }
  11. }
  12. return null;
  13. }

调试信息如下

format_png 3

根据调试信息可以看到真正解析视图的ViewResolver的是InternalResourceViewResolver类,也就是我们经常配置的一项类型

  1. <!-- 定义视图文件解析 -->
  2. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  3. <property name="prefix" value="/WEB-INF/views/" />
  4. <property name="suffix" value=".html" />
  5. </bean>

至此我们就得到了SpringMVC处理请求的完整逻辑

format_png 4

SpringMVC处理请求的整个流程已经梳理清楚了。

但是,有两个重要的问题没有解决,那就是:参数绑定和返回值处理。

因为在编写Controller里面的方法的时候,各种类型的参数都有,SpringMVC是怎么处理不同类型的参数的呢?
SpringMVC处理请求完成后,一定会返回ModelAndView吗,如果加了@ResponseBody注解呢?

参数绑定

在整个流程中,还有一个最重要的方法,那就是真正执行handler的方法,参数的绑定和返回值的处理都在这个方法里,也就是

  1. // Actually invoke the handler.
  2. mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

handle

handle方法的作用是根据请求参数,执行真正的处理方法,并且返回合适的ModelAndView对象,也有可能返回null。该方法定义如下
AbstractHandlerMethodAdapter类中

  1. /**
  2. * This implementation expects the handler to be an {@link HandlerMethod}.
  3. */
  4. @Override
  5. @Nullable
  6. public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  7. throws Exception {
  8. return handleInternal(request, response, (HandlerMethod) handler);
  9. }

可以看到这个方法实现只有一行代码

handleInternal

继续深入handleInternal方法

  1. @Override
  2. protected ModelAndView handleInternal(HttpServletRequest request,
  3. HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  4. ModelAndView mav;
  5. // 校验指定的请求以获取受支持的方法类型(GET、POST等)和所需的session
  6. checkRequest(request);
  7. // Execute invokeHandlerMethod in synchronized block if required.
  8. if (this.synchronizeOnSession) {
  9. HttpSession session = request.getSession(false);
  10. if (session != null) {
  11. Object mutex = WebUtils.getSessionMutex(session);
  12. synchronized (mutex) {
  13. mav = invokeHandlerMethod(request, response, handlerMethod);
  14. }
  15. }
  16. else {
  17. // No HttpSession available -> no mutex necessary
  18. mav = invokeHandlerMethod(request, response, handlerMethod);
  19. }
  20. }
  21. else {
  22. // No synchronization on session demanded at all...
  23. // 真正执行handler的方法
  24. mav = invokeHandlerMethod(request, response, handlerMethod);
  25. }
  26. if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
  27. if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
  28. applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
  29. }
  30. else {
  31. prepareResponse(response);
  32. }
  33. }
  34. return mav;
  35. }

invokeHandlerMethod

继续深入invokeHandlerMethod方法

  1. /**
  2. * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
  3. * if view resolution is required.
  4. * 执行@RequestMapping标注的handler方法,如果需要解析视图就准备一个ModelAndView
  5. */
  6. @Nullable
  7. protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
  8. HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  9. ServletWebRequest webRequest = new ServletWebRequest(request, response);
  10. try {
  11. WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
  12. ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
  13. // HandlerMethod接口封装执行方法的信息,提供对方法参数,方法返回值,方法注释等的便捷访问。
  14. ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
  15. if (this.argumentResolvers != null) {
  16. invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  17. }
  18. if (this.returnValueHandlers != null) {
  19. invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
  20. }
  21. invocableMethod.setDataBinderFactory(binderFactory);
  22. invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
  23. // ModelAndViewContainer可以看做ModelAndView的上下文容器,关联着Model和View的信息
  24. ModelAndViewContainer mavContainer = new ModelAndViewContainer();
  25. mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
  26. modelFactory.initModel(webRequest, mavContainer, invocableMethod);
  27. mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
  28. AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
  29. asyncWebRequest.setTimeout(this.asyncRequestTimeout);
  30. WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  31. asyncManager.setTaskExecutor(this.taskExecutor);
  32. asyncManager.setAsyncWebRequest(asyncWebRequest);
  33. asyncManager.registerCallableInterceptors(this.callableInterceptors);
  34. asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
  35. if (asyncManager.hasConcurrentResult()) {
  36. Object result = asyncManager.getConcurrentResult();
  37. mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
  38. asyncManager.clearConcurrentResult();
  39. LogFormatUtils.traceDebug(logger, traceOn -> {
  40. String formatted = LogFormatUtils.formatValue(result, !traceOn);
  41. return "Resume with async result [" + formatted + "]";
  42. });
  43. invocableMethod = invocableMethod.wrapConcurrentResult(result);
  44. }
  45. // 真正执行Handler的方法
  46. invocableMethod.invokeAndHandle(webRequest, mavContainer);
  47. if (asyncManager.isConcurrentHandlingStarted()) {
  48. return null;
  49. }
  50. // 获取ModelAndeView对象
  51. return getModelAndView(mavContainer, modelFactory, webRequest);
  52. }
  53. finally {
  54. webRequest.requestCompleted();
  55. }
  56. }

invokeAndHandle

invokeAndHandle方法的作用是执行并处理真正响应请求的方法,该方法定义如下

  1. /**
  2. * Invoke the method and handle the return value through one of the
  3. * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
  4. * @param webRequest the current request
  5. * @param mavContainer the ModelAndViewContainer for this request
  6. * @param providedArgs "given" arguments matched by type (not resolved)
  7. */
  8. public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  9. Object... providedArgs) throws Exception {
  10. // 执行handler的方法
  11. Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  12. setResponseStatus(webRequest);
  13. if (returnValue == null) {
  14. if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
  15. disableContentCachingIfNecessary(webRequest);
  16. mavContainer.setRequestHandled(true);
  17. return;
  18. }
  19. }
  20. else if (StringUtils.hasText(getResponseStatusReason())) {
  21. mavContainer.setRequestHandled(true);
  22. return;
  23. }
  24. mavContainer.setRequestHandled(false);
  25. Assert.state(this.returnValueHandlers != null, "No return value handlers");
  26. try {
  27. this.returnValueHandlers.handleReturnValue(
  28. returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
  29. }
  30. catch (Exception ex) {
  31. if (logger.isTraceEnabled()) {
  32. logger.trace(formatErrorForReturnValue(returnValue), ex);
  33. }
  34. throw ex;
  35. }
  36. }

invokeForRequest

  1. /**
  2. * Invoke the method after resolving its argument values in the context of the given request.
  3. * <p>Argument values are commonly resolved through
  4. * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
  5. * The {@code providedArgs} parameter however may supply argument values to be used directly,
  6. * i.e. without argument resolution. Examples of provided argument values include a
  7. * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
  8. * Provided argument values are checked before argument resolvers.
  9. * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
  10. * resolved arguments.
  11. * @param request the current request
  12. * @param mavContainer the ModelAndViewContainer for this request
  13. * @param providedArgs "given" arguments matched by type, not resolved
  14. * @return the raw value returned by the invoked method
  15. * @throws Exception raised if no suitable argument resolver can be found,
  16. * or if the method raised an exception
  17. * @see #getMethodArgumentValues
  18. * @see #doInvoke
  19. */
  20. @Nullable
  21. public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  22. Object... providedArgs) throws Exception {
  23. // 获取参数
  24. Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  25. if (logger.isTraceEnabled()) {
  26. logger.trace("Arguments: " + Arrays.toString(args));
  27. }
  28. // 执行
  29. return doInvoke(args);
  30. }

真正的执行无非就是通过反射invoke,所以更重要的是参数是如何绑定的,详情就在getMethodArgumentValues方法

getMethodArgumentValues

getMethodArgumentValues方法用于从request请求中获取真正的参数,返回的是Object数组,该方法定义如下

  1. /**
  2. * Get the method argument values for the current request, checking the provided
  3. * argument values and falling back to the configured argument resolvers.
  4. * <p>The resulting array will be passed into {@link #doInvoke}.
  5. * @since 5.1.2
  6. */
  7. protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
  8. Object... providedArgs) throws Exception {
  9. // 获取方法上所有的参数
  10. MethodParameter[] parameters = getMethodParameters();
  11. if (ObjectUtils.isEmpty(parameters)) {
  12. return EMPTY_ARGS;
  13. }
  14. Object[] args = new Object[parameters.length];
  15. for (int i = 0; i < parameters.length; i++) {
  16. MethodParameter parameter = parameters[i];
  17. parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
  18. args[i] = findProvidedArgument(parameter, providedArgs);
  19. if (args[i] != null) {
  20. continue;
  21. }
  22. if (!this.resolvers.supportsParameter(parameter)) {
  23. throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
  24. }
  25. try {
  26. args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
  27. }
  28. catch (Exception ex) {
  29. // Leave stack trace for later, exception may actually be resolved and handled...
  30. if (logger.isDebugEnabled()) {
  31. String exMsg = ex.getMessage();
  32. if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
  33. logger.debug(formatArgumentError(parameter, exMsg));
  34. }
  35. }
  36. throw ex;
  37. }
  38. }
  39. return args;
  40. }

format_png 5

根据调试信息可以看到,用来处理请求参数的类是HandlerMethodArgumentResolver接口的实现类HandlerMethodArgumentResolverComposite,此时正在处理的参数是一个Student对象,并且已经把值注绑定了,也就是说真正执行绑定的是方法resolveArgument

搜索公众号程序员小乐回复关键字“Java”,获取Java面试题和答案.pdf。

resolveArgument

resolveArgument是真正执行绑定的的方法

  1. /**
  2. * Iterate over registered
  3. * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}
  4. * and invoke the one that supports it.
  5. * @throws IllegalArgumentException if no suitable argument resolver is found
  6. */
  7. @Override
  8. @Nullable
  9. public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  10. NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  11. // 获取合适的参数解析器
  12. HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
  13. if (resolver == null) {
  14. throw new IllegalArgumentException("Unsupported parameter type [" +
  15. parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
  16. }
  17. // 执行参数绑定
  18. return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
  19. }

getArgumentResolver

getArgumentResolver该方法用于执行参数的绑定,定义如下

  1. /**
  2. * Find a registered {@link HandlerMethodArgumentResolver} that supports
  3. * the given method parameter.
  4. */
  5. @Nullable
  6. private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
  7. HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
  8. if (result == null) {
  9. for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
  10. if (resolver.supportsParameter(parameter)) {
  11. result = resolver;
  12. this.argumentResolverCache.put(parameter, result);
  13. break;
  14. }
  15. }
  16. }
  17. return result;
  18. }

该方法的逻辑就是先从argumentResolver缓存中找到能够执行参数绑定的HandlerMethodArgumentResolver,如果找不到就从HandlerMethodArgumentResolver找,SpringMVC支持的HandlerMethodArgumentResolver一共有26种,用来解析各种类型的参数

format_png 6

根据博主的调试可以知道

  • RequestParamMethodArgumentResolver:处理普通参数(基本类型、包装类型、String),不管加不加@RequestParam注解
  • ServletModelAttributeMethodProcessor:处理POJO类型的参数,比如自定义的Student对象
  • RequestResponseBodyMethodProcessor:处理@RequestBody注解类型的参数

resolveArgument

由于不同类型的参数有不同的HandlerMethodArgumentResolver来处理,此处选取POJO类型参数的注入实现,对应的参数解析类是ModelAttributeMethodProcessor,其中resolveArgument方法用来解析(绑定)参数方法定义如下

  1. /**
  2. * Resolve the argument from the model or if not found instantiate it with
  3. * its default if it is available. The model attribute is then populated
  4. * with request values via data binding and optionally validated
  5. * if {@code @java.validation.Valid} is present on the argument.
  6. * @throws BindException if data binding and validation result in an error
  7. * and the next method parameter is not of type {@link Errors}
  8. * @throws Exception if WebDataBinder initialization fails
  9. */
  10. @Override
  11. @Nullable
  12. public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  13. NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  14. Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
  15. Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
  16. // 获取参数名
  17. String name = ModelFactory.getNameForParameter(parameter);
  18. // 获取参数上的ModelAttribute注解
  19. ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
  20. if (ann != null) {
  21. mavContainer.setBinding(name, ann.binding());
  22. }
  23. Object attribute = null;
  24. BindingResult bindingResult = null;
  25. if (mavContainer.containsAttribute(name)) {
  26. attribute = mavContainer.getModel().get(name);
  27. }
  28. else {
  29. // Create attribute instance
  30. try {
  31. // 创建参数类型的实例(未注入值),底层就是通过反射调用构造方法
  32. attribute = createAttribute(name, parameter, binderFactory, webRequest);
  33. }
  34. catch (BindException ex) {
  35. if (isBindExceptionRequired(parameter)) {
  36. // No BindingResult parameter -> fail with BindException
  37. throw ex;
  38. }
  39. // Otherwise, expose null/empty value and associated BindingResult
  40. if (parameter.getParameterType() == Optional.class) {
  41. attribute = Optional.empty();
  42. }
  43. bindingResult = ex.getBindingResult();
  44. }
  45. }
  46. if (bindingResult == null) {
  47. // Bean property binding and validation;
  48. // skipped in case of binding failure on construction.
  49. WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
  50. if (binder.getTarget() != null) {
  51. if (!mavContainer.isBindingDisabled(name)) {
  52. // 真正执行绑定(值注入)的方法
  53. bindRequestParameters(binder, webRequest);
  54. }
  55. validateIfApplicable(binder, parameter);
  56. if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
  57. throw new BindException(binder.getBindingResult());
  58. }
  59. }
  60. // Value type adaptation, also covering java.util.Optional
  61. if (!parameter.getParameterType().isInstance(attribute)) {
  62. attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
  63. }
  64. bindingResult = binder.getBindingResult();
  65. }
  66. // Add resolved attribute and BindingResult at the end of the model
  67. Map<String, Object> bindingResultModel = bindingResult.getModel();
  68. mavContainer.removeAttributes(bindingResultModel);
  69. mavContainer.addAllAttributes(bindingResultModel);
  70. return attribute;
  71. }

format_png 7

根据调试信息也可以看到bindRequestParameters(binder, webRequest)执行完成之后,POJO类型的参数已经完成了绑定。

bindRequestParameters

  1. /**
  2. * This implementation downcasts {@link WebDataBinder} to
  3. * {@link ServletRequestDataBinder} before binding.
  4. * @see ServletRequestDataBinderFactory
  5. */
  6. @Override
  7. protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
  8. ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
  9. Assert.state(servletRequest != null, "No ServletRequest");
  10. ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
  11. // 执行绑定的方法
  12. servletBinder.bind(servletRequest);
  13. }

bind

继续深入bind方法

  1. public void bind(ServletRequest request) {
  2. // 获取所有参数的键值对
  3. MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
  4. // 处理文件上传请求
  5. MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
  6. if (multipartRequest != null) {
  7. bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
  8. }
  9. // 把url中携带的参数也加入到MutablePropertyValues
  10. addBindValues(mpvs, request);
  11. // 执行绑定(注入值)
  12. doBind(mpvs);
  13. }

由于调用层次过深,所以无法一步步列出下面的步骤,doBind方法的原理还是通过调用POJO对象里的setter方法设置值,可以查看最终的调试信息

format_png 8

根据调试信息可以看到,最终执行的还是POJO对象的setter方法,具体执行的类是BeanWrapperImpl

了解了参数的绑定,再来看返回值的处理。

返回值处理

invokeAndHandle

回到源码invokeAndHandle方法处(ServletInvocableHandlerMethod类中),该方法定义如下

  1. /**
  2. * Invoke the method and handle the return value through one of the
  3. * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
  4. * @param webRequest the current request
  5. * @param mavContainer the ModelAndViewContainer for this request
  6. * @param providedArgs "given" arguments matched by type (not resolved)
  7. */
  8. public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  9. Object... providedArgs) throws Exception {
  10. Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  11. setResponseStatus(webRequest);
  12. if (returnValue == null) {
  13. if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
  14. disableContentCachingIfNecessary(webRequest);
  15. mavContainer.setRequestHandled(true);
  16. return;
  17. }
  18. }
  19. else if (StringUtils.hasText(getResponseStatusReason())) {
  20. mavContainer.setRequestHandled(true);
  21. return;
  22. }
  23. mavContainer.setRequestHandled(false);
  24. Assert.state(this.returnValueHandlers != null, "No return value handlers");
  25. try {
  26. // 真正处理不同类型返回值的方法
  27. this.returnValueHandlers.handleReturnValue(
  28. returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
  29. }
  30. catch (Exception ex) {
  31. if (logger.isTraceEnabled()) {
  32. logger.trace(formatErrorForReturnValue(returnValue), ex);
  33. }
  34. throw ex;
  35. }
  36. }

真正处理不同类型的返回值的方法是handleReturnValue方法

handleReturnValue

  1. /**
  2. * Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.
  3. * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
  4. */
  5. @Override
  6. public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
  7. ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
  8. // 根据返回值个返回值类型选取合适的HandlerMethodReturnValueHandler
  9. HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
  10. if (handler == null) {
  11. throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
  12. }
  13. // 真正的处理返回值
  14. handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
  15. }

selectHandler

  1. @Nullable
  2. private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
  3. boolean isAsyncValue = isAsyncReturnValue(value, returnType);
  4. for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
  5. if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
  6. continue;
  7. }
  8. if (handler.supportsReturnType(returnType)) {
  9. return handler;
  10. }
  11. }
  12. return null;
  13. }

format_png 9

根据调试信息可以看到,SpringMVC为返回值提供了15个HandlerMethodReturnValueHandler的实现了来处理不同类型的返回值。

事实上,用来处理@ResponseBody类型的是RequestResponseBodyMethodProcessor

如果对前文参数绑定还有印象的话,会发现@RequestBody类型参数绑定也是用的这个类。

继续跟进RequestResponseBodyMethodProcessor类的handleReturnValue方法

handleReturnValue

RequestResponseBodyMethodProcessor类的handleReturnValue方法定义如下

这里设置了一个非常重要的属性requestHandled,这个属性关系到是否需要返回ModelAndView对象

  1. @Override
  2. public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
  3. ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
  4. throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
  5. // 设置该请求是否已在处理程序中完全处理,例如@ResponseBody方法不需要视图解析器,此处就可以设置为true。
  6. // 当控制器方法声明类型为ServletResponse或OutputStream的参数时,也可以设置此标志为true。
  7. // 这个属性设置成true之后,上层getModelAndView获取ModelAndView时会返回Null,因为不需要视图。
  8. // 默认值为false
  9. mavContainer.setRequestHandled(true);
  10. ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
  11. ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
  12. // Try even with null return value. ResponseBodyAdvice could get involved.
  13. // 底层就是利用java.io.OutputStreamWriter类把返回值写到网络IO
  14. writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
  15. }

继续深入writeWithMessageConverters方法,一步步调试到最后,底层就是利用java.io.OutputStreamWriter类把返回值写到网络IO

format_png 10

由于handleReturnValuerequestHandled设置成了true,上层在调用getModelAndView方法时会返回null,表示该请求不需要视图。感兴趣的同学自己调试一下便知。

总结

本文主要从源码的阅读和调试的角度,整体的讲解了SpringMVC处理请求的整个流程,并且讲解了参数的绑定以及返回值的处理。相信大家看完后,结合自己的调试信息,会对SpringMVC的请求处理过程有一个更深入的理解。

1e9a61c1795464632dfac98891d82d81.png

欢迎关注“Java大数据修炼之道”,我们分享最有价值的Java的干货文章,助力您成为有思想的Java开发工程师!

a7ca7979f48418aba0bb0eb635911598.gif

  1. 推荐阅读:

1、项目中常用的19条MySQL优化

2、必须收藏!SpringBoot注解大全

3、分享几个开源的高仿项目, 需要的自取

4、老板要求用java将word转为PDF,这么搞?

5、吐血整理!14 个 Spring MVC 顶级技巧!

6、看过来Mybatis:颠覆你心中对事务的理解

7、MySQL:互联网公司常用分库分表方案汇总

8、卧槽,为什么volatile关键字保证不了线程安全啊

9、这21个刁钻的HashMap 面试题,我把阿里面试官吊打了!

10、Spring Boot “内存泄漏”?看看美团大牛是如何排查的

干货分享**:扫码关注下面的公众号后台回复“资源”领取1T编程资源,里面包含各种学习资料。帮助大家更快的学习编程与进阶+资料**

想充电就关注**Java大数据修炼之道**

  1. 每天学习java技术,你想学的Java知识这里都有!**![bcf5ea91b42ce43350fa3de0ee8aa68a.png][]****![eec44562e1d73ef7db92cb79edec392e.gif][]**

fd02244a2ad96ee0072afd3544223214.png

扫码关注我们

获取更多学习资料

视频 | 面经 | 技术 | 电子书 | 职场

发表评论

表情:
评论列表 (有 0 条评论,110人围观)

还没有评论,来说两句吧...

相关阅读