Spring MVC详细源码解析(下篇)

Stella981
• 阅读 567

至此,第二步已经全部结束,接下来要接触到HandlerAdapter。

第四步:

再次回到DispatcherServlet类的doDispatch方法,继续往下走,进到getHandlerAdapter方法中。

在DispatcherServlet类中维护了一个名为handlerAdapters的List集合,里面保存了所有的HandlerAdapter(处理映射器),Spring MVC默认给我们提供了三个:RequestMappingHandlerAdapter、HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter。

我们先复习一下Spring MVC中构建Hanlder(处理器)的四种方法:

  1. 实现Controller接口(或继承AbstractHandler抽象类)。
  2. 实现HttpRequestHandler接口。
  3. 使用@RequestMapping注解。
  4. 继承HttpServlet(实现原始Servlet接口)。

Spring MVC中共有四个处理器适配器:

  1. RequestMappingHandlerAdapter:适配通过@RequestMapping注解创建的Handler。

  2. HttpRequestHandlerAdapter:适配通过实现HttpRequestHandler接口创建的Handler。

  3. SimpleControllerHandlerAdapter:适配通过实现Controller接口(或继承AbstractHandler抽象类)创建的Handler。

  4. SimpleServletHandlerAdapter(不再默认提供):适配通过继承HttpServlet(实现原始Servlet接口)创建的Handler。

    // 决定使用哪个HandlerAdapter(处理映射器)来处理当前Handlerprotected 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");}

可以看出其实HandlerAdapter内部也只是通过Handler的类型来判断是否支持。

// RequestMappingHandlerAdapter类public final boolean supports(Object handler) { return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));}// SimpleControllerHandlerAdapter类public boolean supports(Object handler) { return (handler instanceof Controller);}// HttpRequestHandlerAdapter类public boolean supports(Object handler) { return (handler instanceof HttpRequestHandler);}// SimpleServletHandlerAdapter类public boolean supports(Object handler) { return (handler instanceof Servlet);}

再获得了指定的处理器适配器后,就该执行了该Handler了。

第五步:

适配器在执行Handler之前,会调用HandlerExecutionChain的applyPreHandle方法来触发前置拦截器。接着就是调用HandlerAdapter的handle方法来执行Handler,这里使用的是实现类RequestMappingHandlerAdapter类,而handle方法在其父抽象类AbstractHandlerMethodAdapter类中,最终会调用RequestMappingHandlerAdapter类的handleInternal方法。

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 {   // 省略其他内容...   // 执行前置拦截器   if (!mappedHandler.applyPreHandle(processedRequest, response)) {    return;   }   // 处理器映射器执行Handler方法   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) {   // As of 4.3, we'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);  }  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);   }  } }}

// AbstractHandlerMethodAdapter类public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { return handleInternal(request, response, (HandlerMethod) handler);}

// RequestMappingHandlerAdapter类protected ModelAndView handleInternal(HttpServletRequest request,          HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; 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 {  // 调用Handler方法,看名称带invkoe可知最终是使用反射调用  mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) {  if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {   applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);  }  else {   prepareResponse(response);  } } return mav;}

然后我们需要进入invokeHandlerMethod方法,这个方法非常重要。WebDataBinderFactory对象是用来生产数据绑定器的,ModelFactory对象是用来生成Model模型的。

InvocableHandlerMethod抽象类是是对HandlerMethod接口的扩展,增加了调用能力(doInvoke方法)。这个能力在Spring MVC可是非常非常重要的,它能够在调用的时候,把方法入参的参数都封装进来(从HTTP request里,当然借助的必然是HandlerMethodArgumentResolver)。该类中有一个HandlerMethodArgumentResolverComposite对象存储了所有的HandlerMethodArgumentResolver,用于解析Handler方法的参数。

ServletInvocableHandlerMethod类是对InvocableHandlerMethod抽象类的扩展,它增加了返回值和响应状态码的处理,另外在ServletInvocableHandlerMethod有个内部类ConcurrentResultHandlerMethod继承于它,支持异常调用结果处理,Servlet容器下Controller在查找适配器时发起调用的最终就是ServletInvocableHandlerMethod。该类中有一个HandlerMethodReturnValueHandlerComposite对象存储了所有的HandlerMethodReturnValueHandler,用于解析Handler方法的返回值。

RequestMappingHandlerAdapter类默认提供了27个argumentResolver和15个returnValueResolver。

ModelAndViewContainer类可以把它定义为ModelAndView上下文的容器,它承担着整个请求过程中的数据传递工作,保存着Model和View的相关信息。

// RequestMappingHandlerAdapter类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);  // 使用Handler方法构建一个ServletInvocableHandlerMethod对象,以便下面的方法调用  ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);  // 为ServletInvocableHandlerMethod对象设置参数解析器和返回值解析器  if (this.argumentResolvers != null) {   invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);  }  if (this.returnValueHandlers != null) {   invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);  }  // 为ServletInvocableHandlerMethod对象设置数据绑定器工厂  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);  // 省略其他内容...  // 调用Handler方法  invocableMethod.invokeAndHandle(webRequest, mavContainer);  if (asyncManager.isConcurrentHandlingStarted()) {   return null;  }  // 通过ModelAndViewContainer构建一个ModelAndView对象  return getModelAndView(mavContainer, modelFactory, webRequest); } finally {  webRequest.requestCompleted(); }}

继续进入到invokeAndHandle方法内,结果发现第一行代码,又调用了一个叫invokeForRequest的方法。

// ServletInvocableHandlerMethod类public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,       Object... providedArgs) throws Exception { // 调用Handler方法并获得返回值 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);  // 省略其他内容...  try {  // 使用返回值处理器来处理返回值  this.returnValueHandlers.handleReturnValue(   returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) {  if (logger.isTraceEnabled()) {   logger.trace(formatErrorForReturnValue(returnValue), ex);  }  throw ex; }}

而当进入invokeForRequest方法后,发现它又调用了一个doInvoke方法。在doInvoke方法中,才是真正地通过反射调用了Handler方法,前面的方法可以说都是在做一些准备工作,例如获取方法的参数、创建可调用的方法对象、创建模型视图容器等。

// InvocableHandlerMethod类public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,        Object... providedArgs) throws Exception { // 获取Handler方法的所有参数值 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) {  logger.trace("Arguments: " + Arrays.toString(args)); } // 调用Handler方法 return doInvoke(args);}protected Object doInvoke(Object... args) throws Exception { // 获取到目标Handler方法的Method对象 Method method = getBridgedMethod(); ReflectionUtils.makeAccessible(method); try {  if (KotlinDetector.isSuspendingFunction(method)) {   return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);  }  // 使用反射调用目标Handler方法  return method.invoke(getBean(), args); } catch (IllegalArgumentException ex) {  assertTargetBean(method, getBean(), args);  String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");  throw new IllegalStateException(formatInvokeError(text, args), ex); } catch (InvocationTargetException ex) {  // Unwrap for HandlerExceptionResolvers ...  Throwable targetException = ex.getTargetException();  if (targetException instanceof RuntimeException) {   throw (RuntimeException) targetException;  }  else if (targetException instanceof Error) {   throw (Error) targetException;  }  else if (targetException instanceof Exception) {   throw (Exception) targetException;  }  else {   throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);  } }}

第六步:

看完Handler方法的调用,就该看如何处理方法返回值了。我们再回到invokeAndHandle方法中,下一个关键方法是handleReturnValue,它的作用是使用HandlerMethodReturnValueHandler(Handler方法 返回值处理器)来处理对应的返回值,这里使用的是适配器模式。

因为我这里返回的是一个字符串,所以应该是使用的ViewNameMethodReturnValueHandler返回值处理器。handleReturnValue方法并没有返回值,返回值(包括Model和View)将被存进ModelAndViewContainer容器中,以便后续使用。

// HandlerMethodReturnValueHandlerComposite类public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 选择一个合适的返回值处理器 HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) {  throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } // 使用指定的返回值处理器来处理返回值 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}// 选择一个合适的返回值处理器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;}// 使用指定的返回值处理器来处理返回值public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { if (returnValue instanceof CharSequence) {  String viewName = returnValue.toString();  // 将视图名存入模型视图容器  mavContainer.setViewName(viewName);  if (isRedirectViewName(viewName)) {   mavContainer.setRedirectModelScenario(true);  } } else if (returnValue != null) {  // should not happen  throw new UnsupportedOperationException("Unexpected return type: " +            returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); }}

第七步:

当HandlerMethodReturnValueHandler处理完Handler方法返回值后,就该将处理后的ModelAndView对象返回给DispatcherServlet类了。这一步就是调用RequestMappingHandlerAdapter类中invokeHandlerMethod方法的最后一行代码(即getModelAndView方法),获取ModelAndView对象。

在getModelAndView方法中,会对Model、View做最后的处理,从ModelAndViewContainer容器中取出Model和View来构建一个ModelAndView。

// RequestMappingHandlerAdapter类private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,          ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, .........
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
6个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(