Maison > Java > javaDidacticiel > Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

Libérer: 2023-07-26 16:34:06
avant
754 Les gens l'ont consulté

Avant-propos

Je pense que tout le monde connaît le traitement des requêtes SpringMVC. Cet article est principalement basé sur le processus de traitement des requêtes SpringMVC pour lire et déboguer le code source et résoudre plusieurs problèmes qui ne peuvent pas être expliqués uniquement par les organigrammes.

"

La version Spring utilisée dans cet article est la 5.2.2.RELEASE

"

Neuf composants majeurs

Presque toutes les fonctions de SpringMVC sont complétées par neuf composants principaux, alors comprenez les neuf Composants majeurs Le rôle des composants est très important pour l'apprentissage de SpringMVC.

/** 文件上传解析器 */
private MultipartResolver multipartResolver;

/** 区域解析器,用于国际化 */
private LocaleResolver localeResolver;

/** 主题解析器 */
private ThemeResolver themeResolver;

/** Handler映射信息 */
private List<HandlerMapping> handlerMappings;

/** Handler适配器*/
private List<HandlerAdapter> handlerAdapters;

/** Handler执行异常解析器 */
private List<HandlerExceptionResolver> handlerExceptionResolvers;

/** 请求到视图的转换器 */
private RequestToViewNameTranslator viewNameTranslator;

/** SpringMVC允许重定向时携带参数,存在session中,用完就销毁,所以叫FlashMap */
private FlashMapManager flashMapManager;

/** 视图解析器 */
private List<ViewResolver> viewResolvers;
1234567891011121314151617181920212223242526
Copier après la connexion
  • HandlerMapping : informations de mappage du gestionnaire, recherchez le gestionnaire (Handler) en fonction des informations d'URL contenues dans la demande. Chaque demande nécessite le traitement du gestionnaire correspondant.
  • HandlerAdapter : adaptateur de gestionnaire, SpringMVC n'appelle pas directement le processeur (Handler), mais l'appelle via HandlerAdapter, principalement pour unifier la méthode d'appel de Handler
  • ViewResolver : analyseur de vue, utilisé pour convertir les types de chaînes La vue Le nom se résout en une vue de type View. ViewResolver doit trouver le modèle utilisé pour le rendu et la technologie utilisée (c'est-à-dire le type de vue) pour le rendu, et le processus de rendu spécifique est complété par différentes vues elles-mêmes.
  • MultipartResolver : analyseur de téléchargement de fichiers, principalement utilisé pour traiter les demandes de téléchargement de fichiers. solveur : régional résolveur, utilisé pour prendre en charge l'internationalisation
  • FlashMapManager : SpringMVC permet aux paramètres d'être transportés pendant la redirection, stockés dans la session et détruits après utilisation, il s'appelle donc FlashMap
  • ThemeResolver : résolveur de thème, les trois premiers des neuf composants principaux utilisés pour prendre en charge différents thèmes sont HandlerMapping, HandlerAdapter et ViewResolver, car ce sont les trois composants qui ne peuvent être évités lors de la lecture du code source. Préparation de Debugging
  • Just Construisez un projet de base de Spring Project
  • Controller Part
  • @Controller
    public class IndexController {
    
        @RequestMapping("/index/home")
        public String home(String id, Student student, @RequestParam("code") String code) {
            System.out.println(student.getName());
            return "index";
        }
    
        @ResponseBody
        @RequestMapping("/index/list")
        public String list() {
            return "success";
        }
    }
    Copier après la connexion
  • entity Part
  • rreee
    again, le code source de printemps est très énorme et ne peut être vu que le la forêt ne peut pas être vue par les arbres et nécessite une lecture ciblée, cet article doit donc se concentrer uniquement sur le processus principal.

Méthode de base

Nous savons tous que SpringMVC dispose d'un contrôleur frontal DispatcherServlet pour distribuer les requêtes. La méthode utilisée pour traiter les requêtes est doService. La méthode est définie comme suit

doService

public class Student {

    private String name;
    private Integer gender;

   // getter、setter
}
Copier après la connexion

. doDispatch

doDispatch est la méthode de doService qui est réellement utilisée pour gérer les requêtes

/**
 * 实际处理请求的方法
 */
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);

   // Determine handler for the current request.
   // 为当前请求找到一个合适的处理器(Handler)
   // 返回值是一个HandlerExecutionChain,也就是处理器执行链
   mappedHandler = getHandler(processedRequest);
   if (mappedHandler == null) {
    noHandlerFound(processedRequest, response);
    return;
   }

   // Determine handler adapter for the current request.
   // 根据HandlerExecutionChain携带的Handler找到合适的HandlerAdapter
   HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

   // Process last-modified header, if supported by the handler.
   // 处理GET请求的缓存
   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;
    }
   }

   // 执行拦截器的preHandle方法
   if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
   }

   // Actually invoke the handler.
   // 利用HandlerAdapter来执行Handler里对应的处理方法
   mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

   if (asyncManager.isConcurrentHandlingStarted()) {
    return;
   }

   // 如果没有设置视图,则应用默认的视图名
   applyDefaultViewName(processedRequest, mv);
   // 执行拦截器的postHandle方法
   mappedHandler.applyPostHandle(processedRequest, response, mv);
  }
  catch (Exception ex) {
   dispatchException = ex;
  }
  catch (Throwable err) {
   // As of 4.3, we&#39;re processing Errors thrown from handler methods as well,
   // making them available for @ExceptionHandler methods and other scenarios.
   dispatchException = new NestedServletException("Handler dispatch failed", err);
  }
  // 根据ModelAndView对象解析视图
  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);
   }
  }
 }
}
Copier après la connexion

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

getHandler

该方法定义如下

/**
 * Return the HandlerExecutionChain for this request.
 * 为这个request返回一个HandlerExecutionChain
 */
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
 if (this.handlerMappings != null) {
  for (HandlerMapping mapping : this.handlerMappings) {
   HandlerExecutionChain handler = mapping.getHandler(request);
   if (handler != null) {
    return handler;
   }
  }
 }
 return null;
}
Copier après la connexion

调试信息如下

Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

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

getHandlerAdapter

getHandlerAdapter方法定义如下

/**
  * Return the HandlerAdapter for this handler object.
  * @param handler the handler object to find an adapter for
  * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
  */
 protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  if (this.handlerAdapters != null) {
   for (HandlerAdapter adapter : this.handlerAdapters) {
    if (adapter.supports(handler)) {
     return adapter;
    }
   }
  }
  throw new ServletException("No adapter for handler [" + handler +
    "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
 }
Copier après la connexion

调试信息如下

Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

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

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

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

/**
 * Handle the result of handler selection and handler invocation, which is
 * either a ModelAndView or an Exception to be resolved to a ModelAndView.
 */
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
  @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
  @Nullable Exception exception) throws Exception {

 boolean errorView = false;

 if (exception != null) {
  if (exception instanceof ModelAndViewDefiningException) {
   logger.debug("ModelAndViewDefiningException encountered", exception);
   mv = ((ModelAndViewDefiningException) exception).getModelAndView();
  }
  else {
   Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
   mv = processHandlerException(request, response, handler, exception);
   errorView = (mv != null);
  }
 }

 // Did the handler return a view to render?
 if (mv != null && !mv.wasCleared()) {
  // 主要调用该方法渲染视图
  render(mv, request, response);
  if (errorView) {
   WebUtils.clearErrorRequestAttributes(request);
  }
 }
 else {
  if (logger.isTraceEnabled()) {
   logger.trace("No view rendering, null ModelAndView returned.");
  }
 }

 if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
  // Concurrent handling started during a forward
  return;
 }

 if (mappedHandler != null) {
  // Exception (if any) is already handled..
  mappedHandler.triggerAfterCompletion(request, response, null);
 }
}
Copier après la connexion

render

render方法定义如下

/**
 * Render the given ModelAndView.
 * <p>This is the last stage in handling a request. It may involve resolving the view by name.
 * @param mv the ModelAndView to render
 * @param request current HTTP servlet request
 * @param response current HTTP servlet response
 * @throws ServletException if view is missing or cannot be resolved
 * @throws Exception if there&#39;s a problem rendering the view
 */
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
 // Determine locale for request and apply it to the response.
 Locale locale =
   (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
 response.setLocale(locale);

 View view;
 String viewName = mv.getViewName();
 if (viewName != null) {
  // We need to resolve the view name.
  // 根据给定的视图名称,解析获取View对象
  view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
  if (view == null) {
   throw new ServletException("Could not resolve view with name &#39;" + mv.getViewName() +
     "&#39; in servlet with name &#39;" + getServletName() + "&#39;");
  }
 }
 else {
  // No need to lookup: the ModelAndView object contains the actual View object.
  view = mv.getView();
  if (view == null) {
   throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
     "View object in servlet with name &#39;" + getServletName() + "&#39;");
  }
 }

 // Delegate to the View object for rendering.
 if (logger.isTraceEnabled()) {
  logger.trace("Rendering view [" + view + "] ");
 }
 try {
  if (mv.getStatus() != null) {
   response.setStatus(mv.getStatus().value());
  }
  view.render(mv.getModelInternal(), request, response);
 }
 catch (Exception ex) {
  if (logger.isDebugEnabled()) {
   logger.debug("Error rendering view [" + view + "]", ex);
  }
  throw ex;
 }
}
Copier après la connexion

resolveViewName

resolveViewName方法定义如下

@Nullable
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;
}
Copier après la connexion

调试信息如下

Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

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

<!-- 定义视图文件解析 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
 <property name="prefix" value="/WEB-INF/views/" />
 <property name="suffix" value=".html" />
</bean>
Copier après la connexion

至此我们就得到了SpringMVC处理请求的完整逻辑Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?SpringMVC处理请求的整个流程已经梳理清楚了。

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

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

参数绑定

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

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
Copier après la connexion

handle

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

/**
 * This implementation expects the handler to be an {@link HandlerMethod}.
 */
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
  throws Exception {

 return handleInternal(request, response, (HandlerMethod) handler);
}
Copier après la connexion

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

handleInternal

继续深入handleInternal方法

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
  HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

 ModelAndView mav;
 // 校验指定的请求以获取受支持的方法类型(GET、POST等)和所需的session
 checkRequest(request);

 // Execute invokeHandlerMethod in synchronized block if required.
 if (this.synchronizeOnSession) {
  HttpSession session = request.getSession(false);
  if (session != null) {
   Object mutex = WebUtils.getSessionMutex(session);
   synchronized (mutex) {
    mav = invokeHandlerMethod(request, response, handlerMethod);
   }
  }
  else {
   // No HttpSession available -> no mutex necessary
   mav = invokeHandlerMethod(request, response, handlerMethod);
  }
 }
 else {
  // No synchronization on session demanded at all...
  // 真正执行handler的方法
  mav = invokeHandlerMethod(request, response, handlerMethod);
 }

 if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
  if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
   applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
  }
  else {
   prepareResponse(response);
  }
 }

 return mav;
}
Copier après la connexion

invokeHandlerMethod

继续深入invokeHandlerMethod方法

/**
 * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
 * if view resolution is required.
 * 执行@RequestMapping标注的handler方法,如果需要解析视图就准备一个ModelAndView
 */
@Nullable
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);

  // HandlerMethod接口封装执行方法的信息,提供对方法参数,方法返回值,方法注释等的便捷访问。
  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可以看做ModelAndView的上下文容器,关联着Model和View的信息
  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);
  }

  // 真正执行Handler的方法
  invocableMethod.invokeAndHandle(webRequest, mavContainer);
  if (asyncManager.isConcurrentHandlingStarted()) {
   return null;
  }

  // 获取ModelAndeView对象
  return getModelAndView(mavContainer, modelFactory, webRequest);
 }
 finally {
  webRequest.requestCompleted();
 }
}
Copier après la connexion

invokeAndHandle

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

/**
 * Invoke the method and handle the return value through one of the
 * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
 * @param webRequest the current request
 * @param mavContainer the ModelAndViewContainer for this request
 * @param providedArgs "given" arguments matched by type (not resolved)
 */
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  Object... providedArgs) throws Exception {

 // 执行handler的方法
 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
 setResponseStatus(webRequest);

 if (returnValue == null) {
  if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
   disableContentCachingIfNecessary(webRequest);
   mavContainer.setRequestHandled(true);
   return;
  }
 }
 else if (StringUtils.hasText(getResponseStatusReason())) {
  mavContainer.setRequestHandled(true);
  return;
 }

 mavContainer.setRequestHandled(false);
 Assert.state(this.returnValueHandlers != null, "No return value handlers");
 try {
  this.returnValueHandlers.handleReturnValue(
    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
 }
 catch (Exception ex) {
  if (logger.isTraceEnabled()) {
   logger.trace(formatErrorForReturnValue(returnValue), ex);
  }
  throw ex;
 }
}
Copier après la connexion

invokeForRequest

/**
 * Invoke the method after resolving its argument values in the context of the given request.
 * <p>Argument values are commonly resolved through
 * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
 * The {@code providedArgs} parameter however may supply argument values to be used directly,
 * i.e. without argument resolution. Examples of provided argument values include a
 * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
 * Provided argument values are checked before argument resolvers.
 * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
 * resolved arguments.
 * @param request the current request
 * @param mavContainer the ModelAndViewContainer for this request
 * @param providedArgs "given" arguments matched by type, not resolved
 * @return the raw value returned by the invoked method
 * @throws Exception raised if no suitable argument resolver can be found,
 * or if the method raised an exception
 * @see #getMethodArgumentValues
 * @see #doInvoke
 */
@Nullable
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);
}
Copier après la connexion

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

getMethodArgumentValues

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

/**
 * Get the method argument values for the current request, checking the provided
 * argument values and falling back to the configured argument resolvers.
 * <p>The resulting array will be passed into {@link #doInvoke}.
 * @since 5.1.2
 */
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;
}
Copier après la connexion
Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

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

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

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

resolveArgument
resolveArgument是真正执行绑定的的方法
Copier après la connexion

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

/**
 * Find a registered {@link HandlerMethodArgumentResolver} that supports
 * the given method parameter.
 */
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
 HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
 if (result == null) {
  for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
   if (resolver.supportsParameter(parameter)) {
    result = resolver;
    this.argumentResolverCache.put(parameter, result);
    break;
   }
  }
 }
 return result;
}
Copier après la connexion

该方法的逻辑就是先从argumentResolver缓存中找到能够执行参数绑定的HandlerMethodArgumentResolver,如果找不到就从HandlerMethodArgumentResolver找,SpringMVC支持的HandlerMethodArgumentResolver一共有26种,用来解析各种类型的参数
Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

                                    根据博主的调试可以知道
Copier après la connexion

RequestParamMethodArgumentResolver:处理普通参数(基本类型、包装类型、String),不管加不加@RequestParam注解ServletModelAttributeMethodProcessor:处理POJO类型的参数,比如自定义的Student对象RequestResponseBodyMethodProcessor:处理@RequestBody注解类型的参数有兴趣的同学可以试试更多不同形式的参数

resolveArgument

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

/**
 * Resolve the argument from the model or if not found instantiate it with
 * its default if it is available. The model attribute is then populated
 * with request values via data binding and optionally validated
 * if {@code @java.validation.Valid} is present on the argument.
 * @throws BindException if data binding and validation result in an error
 * and the next method parameter is not of type {@link Errors}
 * @throws Exception if WebDataBinder initialization fails
 */
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
  NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

 Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
 Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");

 // 获取参数名
 String name = ModelFactory.getNameForParameter(parameter);
 // 获取参数上的ModelAttribute注解
 ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
 if (ann != null) {
  mavContainer.setBinding(name, ann.binding());
 }

 Object attribute = null;
 BindingResult bindingResult = null;

 if (mavContainer.containsAttribute(name)) {
  attribute = mavContainer.getModel().get(name);
 }
 else {
  // Create attribute instance
  try {
   // 创建参数类型的实例(未注入值),底层就是通过反射调用构造方法
   attribute = createAttribute(name, parameter, binderFactory, webRequest);
  }
  catch (BindException ex) {
   if (isBindExceptionRequired(parameter)) {
    // No BindingResult parameter -> fail with BindException
    throw ex;
   }
   // Otherwise, expose null/empty value and associated BindingResult
   if (parameter.getParameterType() == Optional.class) {
    attribute = Optional.empty();
   }
   bindingResult = ex.getBindingResult();
  }
 }

 if (bindingResult == null) {
  // Bean property binding and validation;
  // skipped in case of binding failure on construction.
  WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
  if (binder.getTarget() != null) {
   if (!mavContainer.isBindingDisabled(name)) {
    // 真正执行绑定(值注入)的方法
    bindRequestParameters(binder, webRequest);
   }
   validateIfApplicable(binder, parameter);
   if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
    throw new BindException(binder.getBindingResult());
   }
  }
  // Value type adaptation, also covering java.util.Optional
  if (!parameter.getParameterType().isInstance(attribute)) {
   attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
  }
  bindingResult = binder.getBindingResult();
 }

 // Add resolved attribute and BindingResult at the end of the model
 Map<String, Object> bindingResultModel = bindingResult.getModel();
 mavContainer.removeAttributes(bindingResultModel);
 mavContainer.addAllAttributes(bindingResultModel);

 return attribute;
}
Copier après la connexion
Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

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

bindRequestParameters

/**
 * This implementation downcasts {@link WebDataBinder} to
 * {@link ServletRequestDataBinder} before binding.
 * @see ServletRequestDataBinderFactory
 */
@Override
protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
 ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
 Assert.state(servletRequest != null, "No ServletRequest");
 ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
 // 执行绑定的方法
 servletBinder.bind(servletRequest);
}
Copier après la connexion

bind

继续深入bind方法

public void bind(ServletRequest request) {
 // 获取所有参数的键值对
 MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
 // 处理文件上传请求
 MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
 if (multipartRequest != null) {
  bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
 }
 // 把url中携带的参数也加入到MutablePropertyValues
 addBindValues(mpvs, request);
 // 执行绑定(注入值)
 doBind(mpvs);
}
Copier après la connexion

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

Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

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

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

返回值处理

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

/**
 * Invoke the method and handle the return value through one of the
 * configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
 * @param webRequest the current request
 * @param mavContainer the ModelAndViewContainer for this request
 * @param providedArgs "given" arguments matched by type (not resolved)
 */
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
  Object... providedArgs) throws Exception {

 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
 setResponseStatus(webRequest);

 if (returnValue == null) {
  if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
   disableContentCachingIfNecessary(webRequest);
   mavContainer.setRequestHandled(true);
   return;
  }
 }
 else if (StringUtils.hasText(getResponseStatusReason())) {
  mavContainer.setRequestHandled(true);
  return;
 }

 mavContainer.setRequestHandled(false);
 Assert.state(this.returnValueHandlers != null, "No return value handlers");
 try {
  // 真正处理不同类型返回值的方法
  this.returnValueHandlers.handleReturnValue(
    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
 }
 catch (Exception ex) {
  if (logger.isTraceEnabled()) {
   logger.trace(formatErrorForReturnValue(returnValue), ex);
  }
  throw ex;
 }
}
Copier après la connexion

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

handleReturnValue

/**
 * Iterate over registered {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers} and invoke the one that supports it.
 * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found.
 */
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
  ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

 // 根据返回值个返回值类型选取合适的HandlerMethodReturnValueHandler
 HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
 if (handler == null) {
  throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
 }
 // 真正的处理返回值
 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
Copier après la connexion

selectHandler

@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
 boolean isAsyncValue = isAsyncReturnValue(value, returnType);
 for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
  if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
   continue;
  }
  if (handler.supportsReturnType(returnType)) {
   return handler;
  }
 }
 return null;
}
Copier après la connexion
Intervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?

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

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

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

继续跟进RequestResponseBodyMethodProcessor类的handleReturnValue方法handleReturnValueRequestResponseBodyMethodProcessor类的handleReturnValue方法定义如下

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

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
  ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
  throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

 // 设置该请求是否已在处理程序中完全处理,例如@ResponseBody方法不需要视图解析器,此处就可以设置为true。
 // 当控制器方法声明类型为ServletResponse或OutputStream的参数时,也可以设置此标志为true。 
 // 这个属性设置成true之后,上层getModelAndView获取ModelAndView时会返回Null,因为不需要视图。
 // 默认值为false
 mavContainer.setRequestHandled(true);
 ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
 ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);

 // Try even with null return value. ResponseBodyAdvice could get involved.
 // 底层就是利用java.io.OutputStreamWriter类把返回值写到网络IO
 writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
Copier après la connexion

继续深入writeWithMessageConverters方法,一步步调试到最后,底层就是利用java.io.OutputStreamWriter类把返回值写到网络IOIntervieweur : Le processus de traitement des requêtes SpringMVC que vous avez mentionné a été copié à partir d'Internet, n'est-ce pas ?由于handleReturnValue把requestHandled设置成了true,上层在调用getModelAndView方法时会返回null,表示该请求不需要视图。感兴趣的同学自己调试一下便知。

总结

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:Java学习指南
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal