SpringMVC的源码解析

传送门

SpringMVC的源码解析(精品)
Spring6的源码解析(精品)
SpringBoot3框架(精品)
MyBatis框架(精品)
MyBatis-Plus
SpringDataJPA
SpringCloudNetflix
SpringCloudAlibaba(精品)
Shiro
SpringSecurity
java的LOG日志框架
Activiti(敬请期待)
JDK8新特性
JDK9新特性
JDK10新特性
JDK11新特性
JDK12新特性
JDK13新特性
JDK14新特性
JDK15新特性
JDK16新特性
JDK17新特性
JDK18新特性
JDK19新特性
JDK20新特性
JDK21新特性
其他技术文章传送门入口

一 、前言

由于面试问到的比较多,而且做java开发这块还是需要真正掌握的。
现在参考 尚硅谷p22(p26真正开始)-p42视频 总结一下源码分析。
视频参考地址->链接: P26
下面文章不定期更新中…

        // 反射举例
        String s = "HelloWord";
        Class c = String.class;
        Method m = c.getMethod("equals", Object.class);
        Object o = m.invoke(s, "HelloWord");// 本质就是Object o = s.equals("HelloWord")
        System.out.println(o);// true
        // Controller反射举例(SpringMVC的核心思想就是围绕反射进行的)
        Class orgPersonControllerC = OrgPersonController.class;// 知道具体的Controller
        Method orgPersonControllerM = orgPersonControllerC.getMethod("detailOrgPerson", String.class);// 知道具体的方法detailOrgPerson
        Object invoke = orgPersonControllerM.invoke(orgPersonController, "123");// 知道具体的参数值123
        

下面是核心源码中反射的截图,可见SpringMVC的核心思想就是围绕反射进行的,Controller.Method是找方法,然后invoke就是执行反射,整个源码其实就是上面Controller反射举例的超级复杂版本。比如上面的Controller.Method在源码中被封装成了HandlerExecutionChain 。
在这里插入图片描述

二、面试回答总结

鲁班学院总结的找Controller.Method流程:
找Controller.Method的流程
1.扫描整个项目(spring已经做了) 定义一个map集合
2.拿到所有加了@Controller的注解的类
3.遍历类里面所有的方法对象
4.判断方法是否加了@RequestMapping注解
5.把@RequestMapping注解的value作为map集合的key,put进去,把method对象作为value放入map集合
6.根据用户发送的请求 拿到请求中的URI http://localhost:80/test.do 整个地址叫url,其中 test.do 就是 URI
7.URI其实就是@RequestMapping注解中的value,也就是有了之前map的key了,可以get出来值,这个值就是method对象。

SpringMVC源码流程总结:
1.用户请求被DispatcherServlet接收
2.DispatcherServlet通过xml或者注解调用HandlerMapping(映射器)来获得Handler(本质就是Controller.Method)
3.DispatcherServlet依据Handler(就是Controller.Method)选择合适的HandlerAdapter(适配器)执行该Handler
4.执行Controller.Method后一层一层返回ModelAndView给DispatcherServlet
5.DispatcherServlet根据返回的ModelAndView调用ViewResolver(视图解析器,这边有很多种解析器,比如加前缀forword和jsp解析的都是 同一种解析器,重定向前缀的又是一种解析器,themleaf的又是一种解析器)来得到真正的视图View(jsp)返回给用户
注意:HandlerMapping,HandlerAdapter,ViewResolver,View都是接口。

三、源码解析

由于知识点本来就是网状结构,所以看源码一定要先知道总体结构,再去看细枝末节,而且要有足够耐心分析并且记忆一些关键对象,这些关键对象的继承和接口实现关系非常重要。就比如一个2000多人的大家族,如果搞不清楚族谱,随便拉出来一个人不知道叫叔叔还是伯伯或者其他什么,那么就乱套了。读源码就是看亲戚朋友之间的爱恨情仇,梳理清楚关键对象之间的继承、扩展、转化、包装关系,源码就懂了一小半了。

本文以SpringBoot3.1.2中SpringMVC对应版本是6.0.11为例。
本文以http://localhost:8888/user/detailOrgPerson/{id}这个get方法为例。
实际请求:http://localhost:8888/user/detailOrgPerson/123
案例
下面是简单的增删改查在Controller里面的简单业务代码

package com.ours.www.dhr.orgPerson.controller;


import com.ours.www.dhr.orgPerson.controller.mapper.OrgPersonMapper;
import com.ours.www.dhr.orgPerson.controller.param.*;
import com.ours.www.dhr.orgPerson.controller.vo.DetailOrgPersonVO;
import com.ours.www.dhr.orgPerson.controller.vo.SelectOrgPersonListVO;
import com.ours.www.dhr.orgPerson.controller.vo.SelectOrgPersonPageListVO;
import com.ours.www.dhr.orgPerson.service.OrgPersonService;
import com.ours.www.framework.exception.StatusCode;
import com.ours.www.framework.web.R;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@Validated
@RequestMapping("/user")
public class OrgPersonController {

    @Autowired
    private OrgPersonService orgPersonService;
    @Autowired
    private OrgPersonMapper orgPersonMapper;


    // 人员管理-人员新增
    @PostMapping("/insertOrgPerson")
    public R<String> insertOrgPerson(@RequestBody InsertOrgPersonParam insertOrgPersonParam) throws  Exception{
        String res = orgPersonService.insertOrgPerson(insertOrgPersonParam);
        if (StringUtils.isNotEmpty(res)) {
            return R.error(StatusCode.Commons.INTERNAL_SERVER_ERROR,res);
        }
        return R.ok();
    }

    // 人员管理-人员修改
    @PutMapping("/updateOrgPerson")
    public R<String> updateOrgPerson(  @RequestBody UpdateOrgPersonParam updateOrgPersonParam) throws  Exception{
        String res = orgPersonService.updateOrgPerson(updateOrgPersonParam);
        if (StringUtils.isNotEmpty(res)) {
            return R.error(StatusCode.Commons.INTERNAL_SERVER_ERROR,res);
        }
        return R.ok();
    }

    // 人员管理-人员保存(新增和修改同一个接口)
    @PostMapping("/saveOrgPerson")
    public R<String> saveOrgPerson(  @RequestBody SaveOrgPersonParam saveOrgPersonParam) throws  Exception{
        String res = orgPersonService.saveOrgPerson(saveOrgPersonParam);
        if (StringUtils.isNotEmpty(res)) {
            return R.error(StatusCode.Commons.INTERNAL_SERVER_ERROR,res);
        }
        return R.ok();
    }

    // 人员管理-人员删除
    @DeleteMapping("/deleteOrgPerson/{id}")
    public R<String> deleteOrgPerson( @PathVariable String id) throws  Exception{
        String res = orgPersonService.deleteOrgPerson(id);
        if (StringUtils.isNotEmpty(res)) {
            return R.error(StatusCode.Commons.INTERNAL_SERVER_ERROR,res);
        }
        return R.ok();
    }

    // 人员管理-人员详情
    @GetMapping("/detailOrgPerson/{id}")
    public R<DetailOrgPersonVO> detailOrgPerson(@PathVariable String id) {
        DetailOrgPersonVO orgPerson = orgPersonService.detailOrgPerson(id);
        return R.ok(orgPerson);
    }

    // 人员管理-人员列表信息
    @GetMapping("/selectOrgPersonList")
    public R<List<SelectOrgPersonListVO>> selectOrgPersonList(SelectOrgPersonListParam selectOrgPersonListParam) {
        List<SelectOrgPersonListVO> list = orgPersonService.selectOrgPersonList(selectOrgPersonListParam);
        return R.ok(list);
    }

    // 人员管理-人员分页列表信息
    @GetMapping("/selectOrgPersonPageList")
    public R<Page<SelectOrgPersonPageListVO>> selectOrgPersonPageList(SelectOrgPersonPageListParam selectOrgPersonPageList) {
        Page<SelectOrgPersonPageListVO> page = orgPersonService.selectOrgPersonPageList(selectOrgPersonPageList);
        return R.ok(page);
    }


}

返回值R泛型中的DetailOrgPersonVO对象代码

package com.ours.www.dhr.orgPerson.controller.vo;

import com.ours.www.framework.enums.GenderEnum;
import io.swagger.v3.oas.annotations.media.Schema;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

public class DetailOrgPersonVO implements Serializable {

    @Schema(title = "主键id")
    private String id;

    @Schema(title = "姓名")
    private String name;

    @Schema(title = "入职时间")
    private Date startTime;

    @Schema(title = "间断工龄时长")
    private BigDecimal restWorkTime;

    @Schema(title = "性别")
    private GenderEnum genderEnum;

    @Override
    public String toString() {
        return "DetailOrgPersonVO{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", startTime=" + startTime +
                ", restWorkTime=" + restWorkTime +
                ", genderEnum=" + genderEnum +
                '}';
    }

    public GenderEnum getGenderEnum() {
        return genderEnum;
    }

    public void setGenderEnum(GenderEnum genderEnum) {
        this.genderEnum = genderEnum;
    }

    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    public BigDecimal getRestWorkTime() {
        return restWorkTime;
    }

    public void setRestWorkTime(BigDecimal restWorkTime) {
        this.restWorkTime = restWorkTime;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

返回值R对象的代码也说明一下,其他业务代码非常简单,源码解析用不到,就不粘贴出来了。

package com.ours.www.framework.web;

import com.ours.www.framework.exception.StatusCode;
import io.swagger.v3.oas.annotations.media.Schema;


@Schema(title = "响应结果")
public class R<T> {

    @Schema(title = "状态码")
    private int code;

    @Schema(title = "响应描述")
    private String message;

    @Schema(title = "响应数据")
    private T data;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public static <T> R<T> of(int code, String message, T data) {
        R<T> result = new R<>();
        result.setCode(code);
        result.setMessage(message);
        result.setData(data);
        return result;
    }

    public static <T> R<T> of(StatusCode statusCode, T data) {
        return of(statusCode.getCode(), statusCode.getMessage(), data);
    }

    public static <T> R<T> of(StatusCode statusCode, String message, T data) {
        return of(statusCode.getCode(), message, data);
    }

    public static <T> R<T> ok() {
        return of(StatusCode.Commons.OK, null);
    }

    public static <T> R<T> ok(T data) {
        return of(StatusCode.Commons.OK, data);
    }

    public static <T> R<T> error(int code, String message) {
        return of(code, message, null);
    }

    public static <T> R<T> error(StatusCode statusCode) {
        return of(statusCode.getCode(), statusCode.getMessage(), null);
    }

    public static <T> R<T> error(StatusCode statusCode, String message) {
        return of(statusCode, message, null);
    }
}

1、doDispatch概括总结

SpringMVC的入口肯定是DispatcherServlet,核心方法为doService,再核心就是 doDispatch方法了。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;// 请求换个名称,啥也没干
        HandlerExecutionChain mappedHandler = null;// handler的执行链。封装了目标方法的整个信息加上拦截器信息。HandlerExecutionChain 是在HandlerMethod基础上封装了很多拦截器。HanlderMethod就是Controller.Method的详情封装。
        boolean multipartRequestParsed = false;// 是不是文件上传请求,默认不是
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);// 请求有没有异步,使用一个异步管理器

        try {
            try {
                ModelAndView mv = null;
                Exception dispatchException = null;

                try {
                    processedRequest = this.checkMultipart(request);// 检查是否文件上传请求
                    multipartRequestParsed = processedRequest != request;// 如果是文件上传请求,processedRequest就不是原来的request,被修改过了,并将文件上传请求的false改成true。如果不是文件上传请求,不变,还是false。
                    mappedHandler = this.getHandler(processedRequest);// 【核心1】为当前请求决定一个处理器,处理器handler相当于封装了目标方法的整个信息。给handler的执行链赋值,通过当前请求processedRequest找到具体对应handler(handler=Controller)中的具体方法。例如:com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)
                    if (mappedHandler == null) {
                        this.noHandlerFound(processedRequest, response);
                        return;
                    }

                    HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());// 【核心2】为当前请求决定一个处理器适配器,适配器相当于一个大型反射工具。mappedHandler.getHandler()就是HandlerMethod,所以后面参数里面的handler就是HandlerMethod,HandlerMethod本质是Controller.Method,也就是com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)。大部分得到的是RequestMappingHandlerAdapter这个处理器适配器。备注:这边只说程序员开发业务Controller那种情况,主流情况,其实mappedHandler.getHandler()是个Object,有时候是HandlerMethod,有时候是个Controller。(后文会提到)
                    String method = request.getMethod();// 当前请求方法的方式  GET  POST等
                    boolean isGet = HttpMethod.GET.matches(method);// 是否是GET方法
                    if (isGet || HttpMethod.HEAD.matches(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());// 浏览器缓存相关的
                        if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }

                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {// 【核心3】拦截器-方法执行前
                        return;
                    }

                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 【核心4】执行目标方法
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }

                    this.applyDefaultViewName(processedRequest, mv);
                    mappedHandler.applyPostHandle(processedRequest, response, mv);// 【核心5】拦截器-方法正常返回后
                } catch (Exception var20) {
                    dispatchException = var20;
                } catch (Throwable var21) {
                    dispatchException = new ServletException("Handler dispatch failed: " + var21, var21);
                }

                this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);// 【核心6】处理派发结果
            } catch (Exception var22) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);// 【核心7】拦截器-方法是否抛异常都会执行
            } catch (Throwable var23) {
                this.triggerAfterCompletion(processedRequest, response, mappedHandler, new ServletException("Handler processing failed: " + var23, var23));
            }

        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else if (multipartRequestParsed) {
                this.cleanupMultipart(processedRequest);
            }

        }
    }

以上分类了七个核心,下面分别说明这些核心中的一部分。逐步解析源码。

核心1】mappedHandler = this.getHandler(processedRequest);// 为当前请求决定一个处理器,处理器handler相当于封装了目标方法的整个信息加上拦截器信息。给handler的执行链赋值,通过当前请求processedRequest找到具体对应handler(handler=Controller.Method)中的具体方法。例如:com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)

核心2】HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());// 为当前请求决定一个处理器适配器,适配器相当于一个大型反射工具。mappedHandler.getHandler()就是HandlerMethod,就是com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)。大部分得到的是RequestMappingHandlerAdapter这个处理器适配器。
备注:这边只说程序员开发业务Controller那种情况,主流情况,其实mappedHandler.getHandler()是个Object,有时候是HandlerMethod,有时候是个Controller。(后文会提到)

核心3】if (!mappedHandler.applyPreHandle(processedRequest, response)) {// 拦截器-方法执行前

核心4】mv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 执行目标方法

核心5】mappedHandler.applyPostHandle(processedRequest, response, mv);// 拦截器-方法正常返回后

核心6】this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);// 处理派发结果

核心7】this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);// 拦截器-方法是否抛异常都会执行

2、核心1源码分析

HandlerExecutionChain mappedHandler = this.getHandler(processedRequest);
【核心1】为当前请求决定一个处理器,处理器handler相当于封装了目标方法的整个信息加上拦截器信息。给handler的执行链赋值,通过当前请求processedRequest找到具体对应handler(handler=Controller.Method)中的具体方法。
例如:com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)

断点进入mappedHandler = this.getHandler(processedRequest);
在这里插入图片描述
有6个HandlerMapping处理器映射。都是初始化好的,循环这6个,一个一个去找handler。
像localhost:8888会跳转到index.html页面,就是其中的WelcomePageHandlerMapping映射处理的。
index并没有在任何Controller中写具体方法,WelcomePageHandlerMapping处理的路径就是/。
在Controller中写了具体方法的,主要是靠RequestMappingHandlerMapping映射处理的。
在这里插入图片描述
RequestMappingHandlerMapping:保存了所有@RequestMapping和handler的映射规则。
简单点总结:就是通过url从RequestMappingHandlerMapping对象中拿到一个具体的handler,这个handler对象是个执行链,其实本质就是具体Controller中的具体方法。
例如:com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)

断点进入HandlerExecutionChain handler = mapping.getHandler(request);
这步就会出现分支了,如果是循环到这个RequestMappingHandlerMapping,则是后面截图。
如果是循环到WelcomePageHandlerMapping,则是其他代码逻辑。
每一种不同的HandlerMapping实现类,后面的代码截图都是不同的。
后面截图只列举RequestMappingHandlerMapping这个实现类的情况。
在这里插入图片描述

断点进入 return super.getHandlerInternal(request);
在这里插入图片描述
断点进入HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
在这里插入图片描述

@Override
 @Nullable
 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
  String lookupPath = initLookupPath(request);// 得到请求路径。例如:/user/detailOrgPerson/5429783666161851579
  this.mappingRegistry.acquireReadLock();// 得到一把锁
  try {
   HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
   return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
  }
  finally {
   this.mappingRegistry.releaseReadLock();// 释放锁
  }
 }

在这里插入图片描述

@Nullable
 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
  List<Match> matches = new ArrayList<>();
  List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);// 通过url找到对应Controller里面的方法,先不管具体的请求方式,凡是这个url的都找出来
  if (directPathMatches != null) {
   addMatchingMappings(directPathMatches, matches, request);// 上面所有找到的通过一定规则添加到这个matches里面
  }
  if (matches.isEmpty()) {
   addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);// 上一步没有加进去,就这种方式添加到这个matches里面,matches得到的是最佳匹配的url
  }
  if (!matches.isEmpty()) {
   Match bestMatch = matches.get(0);
   if (matches.size() > 1) {// 如果Controller里面有多个方法都匹配这个url,最后通过下面的排序和对比等等操作,会报错。(典型的Controller中写请求地址写重复了报错导致的。也就是正确的应该是一个url只能匹配一个Controller中的方法)
    Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
    matches.sort(comparator);
    bestMatch = matches.get(0);
    if (logger.isTraceEnabled()) {
     logger.trace(matches.size() + " matching mappings: " + matches);
    }
    if (CorsUtils.isPreFlightRequest(request)) {
     for (Match match : matches) {
      if (match.hasCorsConfig()) {
       return PREFLIGHT_AMBIGUOUS_MATCH;
      }
     }
    }
    else {
     Match secondBestMatch = matches.get(1);
     if (comparator.compare(bestMatch, secondBestMatch) == 0) {
      Method m1 = bestMatch.getHandlerMethod().getMethod();
      Method m2 = secondBestMatch.getHandlerMethod().getMethod();
      String uri = request.getRequestURI();
      throw new IllegalStateException(
        "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
     }
    }
   }
   request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
   handleMatch(bestMatch.mapping, lookupPath, request);
   return bestMatch.getHandlerMethod();// 最后得到HandlerMethod。例如:com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)
  }
  else {
   return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
  }
 }

断点持续进入addMatchingMappings(directPathMatches, matches, request);
难点/核心点:核心匹配规则
在这里插入图片描述

最后return bestMatch.getHandlerMethod()
在这里插入图片描述
得到HandlerMethod。
例如:com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)
然后return层层返回,将HandlerMethod封装加入了拦截器信息后变为HandlerExecutionChain。HandlerExecutionChain本质就是Controller.Method上加了很多扩展信息。
HandlerExecutionChain mappedHandler = this.getHandler(processedRequest);执行完成。

铁哥小结:
关键对象:
HandlerMethod:类,Controller.Method的详细封装。

HandlerExecutionChain:HandlerMethod的封装,在HandlerMethod基础上扩展了拦截器信息。

HandlerMapping:处理器映射,鼻祖接口,有6个核心实现类,比如RequestMappingHandlerMapping和WelcomePageHandlerMapping都是它的超级后代。

AbstractHandlerMapping:抽象类,最先实现了HandlerMapping接口,将后代们返回的普通handler也就是HandlerMethod加工拦截器信息进去封装成了HandlerExecutionChain;有两个很争气的儿子AbstractHandlerMethodMapping和AbstractUrlHandlerMapping。

AbstractHandlerMethodMapping:抽象类,继承AbstractHandlerMapping,拥有mappingRegistry这个王牌属性,该属性存放了业务Controller里面各种Method信息,平时程序员在Controller里面写业务方法都在这个属性里面在程序启动的时候登记好了。

RequestMappingHandlerMapping:继承AbstractHandlerMethodMapping,这样也是HandlerMapping接口的实现类,可以认为是HandlerMapping最优秀的后代子孙,平时程序员在Controller里面写业务方法都要通过他,就是他从浏览器中url地址解析出具体的哪个Controller中的哪个Method方法去执行,其实源码大部分解析url的活儿是他老爸AbstractHandlerMethodMapping干的。

AbstractUrlHandlerMapping:抽象类,继承AbstractHandlerMapping,比如localhost:8888会跳转到index.html页面,其实index或者说"/"这个请求并没有在任何Controller中写具体方法,就是AbstractUrlHandlerMapping解析处理的,他会将
"/"这个请求跳转到index.html页面。

WelcomePageHandlerMapping:继承AbstractUrlHandlerMapping,处理欢迎页的,就是处理localhost:8888会跳转到index.html页面,只是源码大部分解析url的活儿是他老爸AbstractUrlHandlerMapping干的。

Match:AbstractHandlerMethodMapping中的内部类,是mappingRegistry这个属性演变而来,通过匹配规则在mappingRegistry众多的url映射中找出和浏览器请求url对应的唯一一个映射并进行封装,getHandlerMethod方法可以得到HandlerMethod,这个时候的HandlerMethod正常下就只有一个。Match长相类似 {GET [/user/detailOrgPerson/{id}]} 这样子(debug断点在idea的代码显示)。

封装流:Controller.Method-》封装为HandlerMethod-》封装为HandlerExecutionChain

难点1:初始化List,HandlerMapping是个接口,有很多个不同实现类。
我们一般都是业务上写Controller,业务方法上加个@RequestMapping,而SpringMVC在启动阶段就去找标记有@Controller和@RequestMapping的方法,并把这些信息封装到List,启动就初始化好了,这个list大小为6,其中以RequestMappingHandlerMapping(HandlerMapping的一个实现类)最为常用,这个list包含了所有的有效url请求地址和实际Controller.Method的映射规则。

难点2:根据不同url循环全部的HandlerMapping接口实现类,比如最常用的RequestMappingHandlerMapping是怎么找到具体handler的。
真正核心代码处理逻辑在HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request)方法中,这个方法中Match匹配规则就是核心逻辑,根据url和具体的匹配规则就能找到对应的handler。在6个HandlerMapping的实现类中,分工不同,比如RequestMappingHandlerMapping继承的属性要多些,例如有mappingRegistry,这个属性存放了业务Controller里面各种Method信息;而其他实现类则没有这个属性,继承父类不同,分工不同,比如localhost:8888会跳转到index.html页面,其实index并没有在任何Controller中写具体方法,循环的时候 在RequestMappingHandlerMapping中没有找到handler,就在WelcomePageHandlerMapping中找,由于继承不同,代码实现不同,不会走HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request)这边的逻辑,而是另一种代码逻辑了。

一句话:根据url拿到对应的HandlerExecutionChain(HandlerExecutionChain本质就是Controller.Method的详细信息封装)。

3、核心2源码分析

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
【核心2】为当前请求决定一个处理器适配器,适配器相当于一个大型反射工具。
mappedHandler.getHandler()就是HandlerMethod,所以后面参数里面的handler就是HandlerMethod,HandlerMethod本质是Controller.Method,也就是com.ours.www.dhr.orgPerson.controller.OrgPersonController#detailOrgPerson(String)。
大部分得到的是RequestMappingHandlerAdapter这个处理器适配器。
备注:这边只说程序员开发业务Controller那种情况,主流情况,其实mappedHandler.getHandler()是个Object,有时候是HandlerMethod,有时候是个Controller。(后文会提到)

断点进入HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
在这里插入图片描述
有4个HandlerAdapter处理器适配器。都是初始化好的,循环这4个,一个一个去找适合处理handler的处理器适配器。
0-》支持方法上标注@RequestMapping
1-》支持函数式编程的
大部分得到的是RequestMappingHandlerAdapter这个处理器适配器。
在这里插入图片描述
断点进入if (adapter.supports(handler)) {
这步就会出现分支,如果是循环到这个RequestMappingHandlerAdapter,则是后面截图。
如果是循环到HttpRequestHandlerAdapter,则是其他代码逻辑。
每一种不同的HandlerAdapter实现类,后面的代码截图都是不同的。
后面截图只列举RequestMappingHandlerAdapter这个实现类的情况。
在这里插入图片描述
由前面可以知道我们的参数handler就是个HandlerMethod,所以前半边肯定是true
在这里插入图片描述
断点进如后半边supportsInternal(handlerMethod)
可以看到是直接写死的true,然后return层层返回,
if (adapter.supports(handler)) 是ture,就找到了相应的HandlerAdapter。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());执行完毕。

铁哥小结:
关键对象:
HandlerAdapter:处理器适配器,鼻祖接口,有4个核心实现类,比如RequestMappingHandlerAdapter是它的超级后代。

AbstractHandlerMethodAdapter:抽象类,最先实现HandlerAdapter接口,有个优秀儿子RequestMappingHandlerAdapter。

RequestMappingHandlerAdapter:继承AbstractHandlerMethodAdapter,也是HandlerAdapter接口的实现类,可以认为是HandlerAdapter接口最优秀的后代子孙,平时程序员在Controller里面写业务方法都要通过他这大型发射工具来invoke执行。

ParameterizableViewController:Controller接口的一个实现类,比如localhost:8888会跳转到index.html页面,在WelcomePageHandlerMapping处理后返回的不是个HandlerMethod,返回了一个Controller。

难点1:初始化List,HandlerAdapter是个接口,有很多个不同实现类。
本来找到handler=Controller.Method了,我们可以直接invoke(对象,参数具体值)这种形式反射处理了,例如前面案例中
Object invoke = orgPersonControllerM.invoke(orgPersonController, “123”);
但是参数五花八门,实际请求中url不止123这一个参数值,可能是非常多个参数值,而且参数类型可能是String、Integer、对象等复杂类型,需要一个强大的反射工具来解析出多个不同类型参数的具体值,为了得到这些个参数具体值,就有了找HandlerAdapter反射工具这一步了。

难点2:根据不同handler循环全部的HandlerAdapter接口实现类,比如最常用的RequestMappingHandlerAdapter是怎么找的。
其实很简单了,基本就是判断handler是什么类型。比如RequestMappingHandlerAdapter就是判断了handler instanceof HandlerMethod。
HttpRequestHandlerAdapter就是判断了handler instanceof HttpRequestHandler。
SimpleControllerHandlerAdapter就是判断了handler instanceof Controller;localhost:8888会跳转到index.html页面,在核心1逻辑中返回的就不是HandlerMethod了,是一个ParameterizableViewController,这个类就是个Controller接口的实现类。在4个HandlerAdapter的实现类中,分工不同,得到的也不同。

4、核心3源码分析

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
【核心3】拦截器-方法执行前

5、核心4源码分析

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
【核心4】执行目标方法,这步会进入到Controller里面,执行具体方法的具体业务代码

断点进入mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
在这里插入图片描述
断点进入mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
核心2获取的Adapter不同,这边分支路线也不同。这边以RequestMappingHandlerAdapter为主。
断点进入return handleInternal(request, response, (HandlerMethod) handler);
在这里插入图片描述

@Override
 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 {
   // No synchronization on session demanded at all...
   mav = invokeHandlerMethod(request, response, handlerMethod);// 执行handler的方法,执行目标方法。
  }

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

  return mav;
 }

断点进入mav = invokeHandlerMethod(request, response, handlerMethod);
执行handler的方法,执行目标方法。
在这里插入图片描述

@Nullable
 protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
   HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

  ServletWebRequest webRequest = new ServletWebRequest(request, response);// 一些初始化操作
  WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
  ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

  ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// Servlet可执行处理器方法,将handlerMethod又封装了一次,封装为可执行方法invocableMethod 。
  if (this.argumentResolvers != null) {// 给invocableMethod设置参数解析器,确定将要执行的目标方法的每一个参数的具体值是什么。这个SpringMVC6.0.11版本有31个参数解析器。
   invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  }
  if (this.returnValueHandlers != null) {// 给invocableMethod设置返回值处理器,确定将要执行的目标方法的返回值,这个SpringMVC6.0.11版本有有15个返回值处理器。
   invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
  }
  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);

  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);
  }

  invocableMethod.invokeAndHandle(webRequest, mavContainer);// 【核心-执行处理分支线】执行并处理,真正执行模板方法。返回的所有数据都放到了mavContainer对象中,这个是模型和视图容器对象,包含了要去的页面地址View和模块Model数据。
  if (asyncManager.isConcurrentHandlingStarted()) {
   return null;
  }

  return getModelAndView(mavContainer, modelFactory, webRequest);// 【核心-获取ModelAndView对象分支线】获取ModelAndView对象。
 }

参数解析器,确定将要执行的目标方法的每一个参数的具体值是什么,这个SpringMVC6.0.11版本有31个参数解析器。
在这里插入图片描述
返回值处理器,确定将要执行的目标方法的返回值,这个SpringMVC6.0.11版本有有15个返回值处理器。
在这里插入图片描述
由于接下来的文章分支线非常多,每个分支线我做了特别说明,结合代码查看,绘制脑图要容易理解一些。
特别帅气的图

我们来看【核心-执行处理分支线】
断点进入invocableMethod.invokeAndHandle(webRequest, mavContainer);
在这里插入图片描述

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
   Object... providedArgs) throws Exception {

  Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 【核心-真正执行目标方法分支线】真正执行目标方法,这步会进入Controller里面,执行Controller里面的代码并将目标方法返回值赋值给returnValue。
  setResponseStatus(webRequest);// 设置响应状态

  if (returnValue == null) {// 如果Controller返回结果为空,下面判断后就直接返回了。
   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;
  }
 }

我们来看【核心-真正执行目标方法分支线】
断点进入Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
【核心-真正执行目标方法分支线】真正执行目标方法,这步会进入Controller里面,
执行Controller里面的代码并将目标方法返回值赋值给returnValue。
在这里插入图片描述

@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);// 【核心-反射工具分支线】利用反射调用目标方法
 }

我们来看【核心-参数处理分支线】
断点进入Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs)
【核心-参数处理分支线】获取方法的所有参数的具体值
在这里插入图片描述

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];// 声明一个args数组,就是最后返回的参数具体值的数组。后面代码发射method.invoke(getBean(), args)用的就是这个args参数。
  for (int i = 0; i < parameters.length; i++) {// 循环parameters数组
   MethodParameter parameter = parameters[i];// 方法上第i个参数的详情信息(注解、索引位置、参数类型等等)
   parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);// 初始化参数名字发现器,来确定参数的名字
   args[i] = findProvidedArgument(parameter, providedArgs);// 赋值,这儿是个null,providedArgs从一开始过来就是null,所以这边并没有赋值到什么。InvocableHandlerMethod其他子类可能会传个有值的providedArgs。
   if (args[i] != null) {// 有值就退出当前循环并进行下一次循环了,这边是null,没有退出,代码继续往下。
    continue;
   }
   if (!this.resolvers.supportsParameter(parameter)) {// 【核心-参数解析判断是否支持分支线】挨个判断所有31个参数解析器,看哪个参数解析器支持解析这个参数
    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;
 }

我们来看【核心-参数解析判断是否支持分支线】
断点持续进入if (!this.resolvers.supportsParameter(parameter)) {
在这里插入图片描述

@Override
 public boolean supportsParameter(MethodParameter parameter) {
  return getArgumentResolver(parameter) != null;
 }




 @Nullable
 private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
  HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);// 从缓存中获取参数解析器,首次请求进来缓存都是空的
  if (result == null) {
   for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {// 31个参数解析器循环解析
    if (resolver.supportsParameter(parameter)) {// 每一个解析器都有自己独特的supportsParameter函数做具体处理
     result = resolver;
     this.argumentResolverCache.put(parameter, result);// 循环所有的31个参数解析器看是否支持这个参数,一旦找到支持的就放到缓存里面,比如PathVariableMethodArgumentResolver参数解析器支持,parameter参数对象是参数的非常详细封装,所以这边put进去的key是非常细粒度的参数对象,例如http://localhost:8888/user/detailOrgPerson/123这个请求,缓存的也只是这个Controller的这个Method方法的这个带PathVariable注解的这个String类型的这个参数。虽然细粒度,理论上一个Controller.Method只要请求过一次,基本上这个方法的所有参数都缓存好了,下次即使参数的具体值不一样,也能从缓存中取到对应的参数解析器处理。所以每一个方法的请求,首次请求参数解析器缓存中都没有,二次请求就都有了。
     break;
    }
   }
  }
  return result;
 }

循环所有的31个参数解析器看是否支持这个参数,一旦找到支持的就放到缓存里面,比如PathVariableMethodArgumentResolver参数解析器支持,parameter参数对象是参数的非常详细封装,所以这边放进缓存的是非常细粒度的参数对象,例如http://localhost:8888/user/detailOrgPerson/123这个请求,缓存的也只是这个Controller的这个Method方法的这个带PathVariable注解的这个String类型的这个参数。虽然细粒度,理论上一个Controller.Method只要请求过一次,基本上这个方法的所有参数都缓存好了,下次即使参数的具体值不一样,也能从缓存中取到对应的参数解析器处理。所以每一个方法的请求,首次请求参数解析器缓存中都没有,二次请求就都有了。

这也是SpringMVC第一次请求相对比较慢,随着缓存的东西越来越多以后,后面请求会越来越快的原因之一。

由于31个参数解析器情况大同小异,下面只举例3个参数解析器的具体支持判断的代码:
PathVariableMethodArgumentResolver参数解析器
在这里插入图片描述

@Override
 public boolean supportsParameter(MethodParameter parameter) {
  if (!parameter.hasParameterAnnotation(PathVariable.class)) {// 参数没有标记PathVariable注解就返回false了,代码继续循环其他参数解析器
   return false;
  }
  if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {// 参数的类型是不是一个Map类型。isAssignableFrom表明Map是parameter.nestedIfOptional().getNestedParameterType()父类或接口,也可以是同一个类或接口。意思是参数类型是Map的子类或者同类或者实现类。这里nested表示嵌套,其实是判断参数的嵌套参数的类型是不是一个Map类型。
   PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);// 获取参数的PathVariable注解对象
   return (pathVariable != null && StringUtils.hasText(pathVariable.value()));// PathVariable注解对象不为空  并且  PathVariable注解对象的值不为空(就是参数有具体值)
  }
  return true;
 }

PathVariableMapMethodArgumentResolver参数解析器
在这里插入图片描述

@Override
 public boolean supportsParameter(MethodParameter parameter) {
  PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);// 获取参数的PathVariable注解对象
  return (ann != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
    !StringUtils.hasText(ann.value()));// PathVariable注解对象不为空 并且 参数类型是Map的子类或者同类或者实现类 并且 PathVariable注解对象的值不为空(就是参数有具体值)
 }

RequestParamMethodArgumentResolver参数解析器
在这里插入图片描述

@Override
 public boolean supportsParameter(MethodParameter parameter) {
  if (parameter.hasParameterAnnotation(RequestParam.class)) {// 参数标记了RequestParam注解
   if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {// 参数类型是Map的子类或者同类或者实现类
    RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);// 获取RequestParam注解对象
    return (requestParam != null && StringUtils.hasText(requestParam.name()));// RequestParam注解对象不为空  并且  RequestParam注解对象名称有值
   }
   else {
    return true;
   }
  }
  else {// 参数没有标记RequestParam注解
   if (parameter.hasParameterAnnotation(RequestPart.class)) {// 参数标记了RequestPart注解直接返回false,代码继续循环其他参数解析器
    return false;
   }
   parameter = parameter.nestedIfOptional();// 获取嵌套参数
   if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {// 参数是文件上传请求类型,就用该解析器处理
    return true;
   }
   else if (this.useDefaultResolution) {
    return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
   }
   else {
    return false;
   }
  }
 }

【核心-参数解析判断是否支持分支线】支线执行完毕后,接下来就是【核心-参数解析处理分支线】。
我们来看【核心-参数解析处理分支线】
断点进入args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory)
在这里插入图片描述

@Override
 @Nullable
 public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
   NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

  HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);// 前面判断哪个参数解析器能支持解析参数的时候,就放到缓存了,所以这步直接可以从缓存取出来
  if (resolver == null) {
   throw new IllegalArgumentException("Unsupported parameter type [" +
     parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
  }
  return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);// 具体解析器的具体resolveArgument实现方法逻辑中处理
 }

断点进入return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
注意:这边有很多分支情况,由于我们的请求案例是PathVariableMethodArgumentResolver这个参数解析器支持,所以进入的是他的resolveArgument方法,由于他没有覆写父类的这个方法,所以真正进入的是他父类AbstractNamedValueMethodArgumentResolver的resolveArgument方法中了。
在这里插入图片描述

@Override
 @Nullable
 public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
   NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

  NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);// 获取参数的名字,就是获取形参对象,例如我们案例http://localhost:8888/user/detailOrgPerson/123这个请求,对应方法的形参是@PathVariable String id  ,本质是根据@PathVariable注解去拿到的这个形参,所以这个形参对象里面的name="id"
  MethodParameter nestedParameter = parameter.nestedIfOptional();// 方法参数对象的嵌套方法参数对象,和上面的parameter一样,都是同一个对象,debug断点中长相是 method 'detailOrgPerson' parameter 0,表示detailOrgPerson方法的第0号参数这个整体对象,因为我们的案例http://localhost:8888/user/detailOrgPerson/123这个请求只有一个参数,形参是id,实参是123。

  Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);// 解析嵌入值和表达式,因为我们案例的形参简单,这边的resolvedName=namedValueInfo.name=id。
  if (resolvedName == null) {
   throw new IllegalArgumentException(
     "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
  }

  Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);// 确定参数值。例如:123。下面代码为一些默认处理。
  if (arg == null) {
   if (namedValueInfo.defaultValue != null) {
    arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
   }
   else if (namedValueInfo.required && !nestedParameter.isOptional()) {
    handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
   }
   arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
  }
  else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
   arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
  }

  if (binderFactory != null) {
   WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
   try {
    arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
   }
   catch (ConversionNotSupportedException ex) {
    throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
      namedValueInfo.name, parameter, ex.getCause());
   }
   catch (TypeMismatchException ex) {
    throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
      namedValueInfo.name, parameter, ex.getCause());
   }
   // Check for null value after conversion of incoming argument value
   if (arg == null && namedValueInfo.defaultValue == null &&
     namedValueInfo.required && !nestedParameter.isOptional()) {
    handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
   }
  }

  handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);

  return arg;
 }
 

断点进入Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
确定参数值。例如:123
在这里插入图片描述

@Override
 @SuppressWarnings("unchecked")
 @Nullable
 protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
  Map<String, String> uriTemplateVars = (Map<String, String>) request.getAttribute(
    HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);// 前面的UrlPathHelper的时候已经把参数等放到了request中,所以这边可以直接取出。例如:Map<id,123>
  return (uriTemplateVars != null ? uriTemplateVars.get(name) : null);// 通过name=id取出123
 }

最后是从request域中取值,取出实参。这个只是PathVariableMethodArgumentResolver参数解析器这个处理方式,不同解析器解析处理方式不同。例如RequestHeaderMethodArgumentResolver参数解析器,String[] headerValues = request.getHeaderValues(name);用原生api获取实参,就不同于PathVariableMethodArgumentResolver。

从request域中取出实参后return层层返回,Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);代码执行完毕,整个【核心-参数处理分支线】执行完毕。 我们回到上面该支线位置。
按照案例,这个时候Object[] args = [123]
在这里插入图片描述

@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);// 【核心-反射工具分支线】利用反射调用目标方法
 }

我们来看【核心-反射工具分支线】
断点进入 return doInvoke(args);
【核心-反射工具分支线】利用反射调用目标方法
在这里插入图片描述
可以看到return method.invoke(getBean(), args);反射语法,呼应文章开头前言所说的SpringMVC的核心思想就是围绕反射进行的,这句代码执行后,就进入到Controller里面去执行业务代码了。return层层返回,return method.invoke(getBean(), args);代码执行完毕,整个【核心-反射工具分支线】执行完毕,也意味着【核心-真正执行目标方法分支线】执行完毕。

我们回到开头来看【核心-真正执行目标方法分支线】,该支线执行完毕,接下来就是【核心-返回值处理分支线】的执行了。
在这里插入图片描述

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
   Object... providedArgs) throws Exception {

  Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 【核心-真正执行目标方法分支线】真正执行目标方法,这步会进入Controller里面,执行Controller里面的代码并将目标方法返回值赋值给returnValue。
  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;
  }
 }

我们来看【核心-返回值处理分支线】,这边设计思想和参数解析器非常相似,也是先判断是否支持,然后用支持的处理器去处理返回值。
断点进入this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
【核心-返回值处理分支线】
在这里插入图片描述

@Override
 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);
// 【核心-返回值处理器处理分支线】
 }

我们来看【核心-返回值处理器判断是否支持分支线】
断点进入HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
【核心-返回值处理器判断是否支持分支线】
在这里插入图片描述

@Nullable
 private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
  boolean isAsyncValue = isAsyncReturnValue(value, returnType);// 是否是异步返回值,按照我们的请求案例,这边是false
  for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
   if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
    continue;
   }
   if (handler.supportsReturnType(returnType)) {// 【核心】每一个返回值处理器都有自己独特的supportsReturnType函数做具体处理
    return handler;
   }
  }
  return null;
 }




private boolean isAsyncReturnValue(@Nullable Object value, MethodParameter returnType) {
  for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
   if (handler instanceof AsyncHandlerMethodReturnValueHandler asyncHandler &&
     asyncHandler.isAsyncReturnValue(value, returnType)) {
    return true;
   }
  }
  return false;// 按照请求案例,挨个判断发现都不是异步返回值处理器,返回false了。
 }

断点持续进入if (handler.supportsReturnType(returnType)) {
每一个返回值处理器都有自己独特的supportsReturnType函数做具体处理
循环所有的15个返回值处理器看是否支持这个返回值,比如RequestResponseBodyMethodProcessor返回值处理器支持,注意这边不像参数解析的时候会放入缓存中,返回值处理器判断逻辑这边没有放入到缓存中。
SpringMVC支持的返回值类型:ModelAndView、Model、View、ResponseEntity、ResponseBodyEmitter、StreamingResponseBody( 函数式接口类型的)、HttpEntity、HttpHeaders、Callable(异步类型的)、DeferredResult(异步类型的)、ListenableFuture(异步类型的)、CompletionStage(异步类型的)、WebAsyncTask(异步任务的)、@ModelAttribute(返回值标记了注解)、@ResponseBody(返回值标记了注解)

由于15个返回值处理器情况大同小异,下面只举例3个返回值处理器的具体支持判断的代码:

ModelAndViewMethodReturnValueHandler返回值处理器
在这里插入图片描述

@Override
 public boolean supportsReturnType(MethodParameter returnType) {
  return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
 }// 返回值的类型是ModelAndView。isAssignableFrom具体的是指返回值类型的父类、同类、接口实现类型是ModelAndView。

ResponseBodyEmitterReturnValueHandler返回值处理器
在这里插入图片描述

@Override
 public boolean supportsReturnType(MethodParameter returnType) {
  Class<?> bodyType = ResponseEntity.class.isAssignableFrom(returnType.getParameterType()) ?
    ResolvableType.forMethodParameter(returnType).getGeneric().resolve() :
    returnType.getParameterType();// 三目运算符判断,确定bodyType的值,相当于是返回值的类型。例如我们案例请求是com.ours.www.dhr.orgPerson.controller.vo.DetailOrgPersonVO.class

  return (bodyType != null && (ResponseBodyEmitter.class.isAssignableFrom(bodyType) ||
    this.reactiveHandler.isReactiveType(bodyType)));// bodyType是ResponseBodyEmitter类型,并且是响应式的。
 }

RequestResponseBodyMethodProcessor返回值处理器/参数解析器(最常用)

在这里插入图片描述

@Override
 public boolean supportsParameter(MethodParameter parameter) {
  return parameter.hasParameterAnnotation(RequestBody.class);// 参数标记了RequestBody注解。
 }

 @Override
 public boolean supportsReturnType(MethodParameter returnType) {
  return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
    returnType.hasMethodAnnotation(ResponseBody.class));// 返回值或者方法上或者类上标记了ResponseBody注解。
 }

【核心-返回值处理器判断是否支持分支线】执行完毕后,接下来就是【核心-返回值处理器处理分支线】。
我们来看【核心-返回值处理器处理分支线】
断点进入handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
【核心-返回值处理器处理分支线】
在这里插入图片描述

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

  mavContainer.setRequestHandled(true);
  ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
  ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// webRequest是原生请求和返回的包装,这里面又进行了一次包装。

  if (returnValue instanceof ProblemDetail detail) {
   outputMessage.setStatusCode(HttpStatusCode.valueOf(detail.getStatus()));
   if (detail.getInstance() == null) {
    URI path = URI.create(inputMessage.getServletRequest().getRequestURI());
    detail.setInstance(path);
   }
  }

  // Try even with null return value. ResponseBodyAdvice could get involved.
  writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);// 【核心】使用消息转化器进行写出操作
 }

注意:这边有很多分支情况,由于我们的请求案例是RequestResponseBodyMethodProcessor这个返回值处理器支持,所以进入的是他的handleReturnValue方法。
断点进入writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);

在这里插入图片描述

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {

		Object body;
		Class<?> valueType;
		Type targetType;

		if (value instanceof CharSequence) {// value是反射处理Controller业务逻辑代码后的返回值。比如我们案例返回的是一个 R<DetailOrgPersonVO>,那么value=R@15995,这个就是个R对象。R对象是我们自己定义的一个返回值类,典型的code、message、data三属性。所以这里if判断不满足,value不是字符串类型。
			body = value.toString();
			valueType = String.class;
			targetType = String.class;
		}
		else {
			body = value;// 拿到返回对象。Object body=R@15995,赋值R对象给body。
			valueType = getReturnValueType(body, returnType);// 拿到返回对象类型。Class<?> valueType = com.ours.www.framework.web.R.class
			targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());// 拿到要转化成目标的类型。Type targetType = com.ours.www.framework.web.R<com.ours.www.dhr.orgPerson.controller.vo.DetailOrgPersonVO>
		}

		if (isResourceType(value, returnType)) {// 判断value是不是资源类型。点进去看判断逻辑也很简单,就是判断是不是InputStreamResource.class或者 Resource.class这种流数据,显然我们案例不是,if判断不满足。
			outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");
			if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&
					outputMessage.getServletResponse().getStatus() == 200) {
				Resource resource = (Resource) value;
				try {
					List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
					outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());
					body = HttpRange.toResourceRegions(httpRanges, resource);
					valueType = body.getClass();
					targetType = RESOURCE_REGION_LIST_TYPE;
				}
				catch (IllegalArgumentException ex) {
					outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());
					outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());
				}
			}
		}

		MediaType selectedMediaType = null;// 选中的媒体类型。这里有个内容协商概念,就是浏览器发请求的时候,Request Headers中有个Accept属性,告诉了服务器浏览器能接受什么样的返回类型,其中典型的*/*表示服务器给啥都能接受,q=0.8这个表示权重,越大权重越高,优先级越高。
		MediaType contentType = outputMessage.getHeaders().getContentType();// 从响应头获取内容类型,前面代码逻辑可能处理后有了,我们案例这边是还没有的
		boolean isContentTypePreset = contentType != null && contentType.isConcrete();// 判断有了
		if (isContentTypePreset) {// 有了就直接赋值了
			if (logger.isDebugEnabled()) {
				logger.debug("Found 'Content-Type:" + contentType + "' in response");
			}
			selectedMediaType = contentType;
		}
		else {// 没有,进入下面逻辑,我们案例走下面逻辑
			HttpServletRequest request = inputMessage.getServletRequest();// 拿到原生request请求
			List<MediaType> acceptableTypes;
			try {
				acceptableTypes = getAcceptableMediaTypes(request);// 得到能接受的所有内容类型,一般有多个,就是内容协商概念中浏览器能接受什么样的返回类型,就是浏览器Request Headers中的Accept属性。备注:如果是apifox发起的请求,不是浏览器发起的,可能只有一个 */*
			}
			catch (HttpMediaTypeNotAcceptableException ex) {
				int series = outputMessage.getServletResponse().getStatus() / 100;
				if (body == null || series == 4 || series == 5) {
					if (logger.isDebugEnabled()) {
						logger.debug("Ignoring error response content (if any). " + ex);
					}
					return;
				}
				throw ex;
			}

			List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);// 当前服务器能够响应的类型,一般有多个,例如application/json 和 application/*+json
			if (body != null && producibleTypes.isEmpty()) {
				throw new HttpMessageNotWritableException(
						"No converter found for return value of type: " + valueType);
			}

			List<MediaType> compatibleMediaTypes = new ArrayList<>();// 浏览器和服务器响应类型匹配后的集合,就是存放两端都支持的类型,两端类型的交集。
			determineCompatibleMediaTypes(acceptableTypes, producibleTypes, compatibleMediaTypes);// 服务器能响应的类型和浏览器能接受的类型进行匹配。点进去就是个嵌套for循环匹配,由于浏览器一般有*/*,基本最后得到的都是服务器能够响应的类型。

			// For ProblemDetail, fall back on RFC 7807 format
			if (compatibleMediaTypes.isEmpty() && ProblemDetail.class.isAssignableFrom(valueType)) {
				determineCompatibleMediaTypes(this.problemMediaTypes, producibleTypes, compatibleMediaTypes);
			}

			if (compatibleMediaTypes.isEmpty()) {
				if (logger.isDebugEnabled()) {
					logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
				}
				if (body != null) {
					throw new HttpMediaTypeNotAcceptableException(producibleTypes);
				}
				return;
			}

			MimeTypeUtils.sortBySpecificity(compatibleMediaTypes);

			for (MediaType mediaType : compatibleMediaTypes) {// 循环匹配的类型,就是上面的交集,给selectedMediaType选定一个媒体类型
				if (mediaType.isConcrete()) {
					selectedMediaType = mediaType;// 我们案例这边会得到application/json类型,也就是我们常说的json类型
					break;
				}
				else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {
					selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
					break;
				}
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Using '" + selectedMediaType + "', given " +
						acceptableTypes + " and supported " + producibleTypes);
			}
		}

		if (selectedMediaType != null) {
			selectedMediaType = selectedMediaType.removeQualityValue();
			for (HttpMessageConverter<?> converter : this.messageConverters) {// 循环所有9个消息转化器,看哪个消息转化器能把返回值对象转化成选定的媒体类型。HttpMessageConverter<T>是个接口,是消息转化器,该接口主要功能就是看是否支持将此Class类型的对象,转化为MediaType类型的数据。例如我们案例这种,是否能将返回值R.class转为json数据,而且也能将json数据转为R.class。
				GenericHttpMessageConverter genericConverter =
						(converter instanceof GenericHttpMessageConverter ghmc ? ghmc : null);// GenericHttpMessageConverter是个接口,继承了HttpMessageConverter<T>,比父接口少了几个方法。例如9个消息转化器中第一个ByteArrayHttpMessageConverter并没有实现GenericHttpMessageConverter,所以这边可能是null。我们案例genericConverter=MappingJackson2HttpMessageConverter消息转化器。
				if (genericConverter != null ?
						((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {// 【核心-canWrite分支线】根据我们案例,MappingJackson2HttpMessageConverter消息转化器来进行处理。 genericConverter不是null就将消息转化器强转一下再判断能否写出(我们案例是这个),否则直接判断能否写出。其中参数valueType是返回对象类型,我们案例是valueType=Class<?> valueType = com.ours.www.framework.web.R.class。
					body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
							inputMessage, outputMessage);// body返回对象-》需要响应的内容
					if (body != null) {
						Object theBody = body;
						LogFormatUtils.traceDebug(logger, traceOn ->
								"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
						addContentDispositionHeader(inputMessage, outputMessage);// 响应内容加一些header信息
						if (genericConverter != null) {
							genericConverter.write(body, targetType, selectedMediaType, outputMessage);// 【核心-write分支线】
						}
						else {
							((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
						}
					}
					else {
						if (logger.isDebugEnabled()) {
							logger.debug("Nothing to write: null body");
						}
					}
					return;
				}
			}
		}

		if (body != null) {
			Set<MediaType> producibleMediaTypes =
					(Set<MediaType>) inputMessage.getServletRequest()
							.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);

			if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {
				throw new HttpMessageNotWritableException(
						"No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");
			}
			throw new HttpMediaTypeNotAcceptableException(getSupportedMediaTypes(body.getClass()));
		}
	}

在这里插入图片描述
9个messageConverters消息转化器
0-》支持返回值类型为btye[]的
1-》String
2-》String
3-》Resource
4-》ResourceRegion
5-》DOMSource、SAXSource、StAXSource、StreamSource、Source
6-》MultiValueMap
7-》所有类型都支持
8-》所有类型都支持
9-》@XmlRootElement注解
循环所有的9个消息转化器,每个消息转化器有自己的canWrite方法和write方法,
设计思路与参数解析器和返回值处理器一样,都是先判断能否支持,然后处理。

我们来看【核心-canWrite分支线】
断点进入if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
【核心-canWrite分支线】根据我们案例,MappingJackson2HttpMessageConverter消息转化器来进行处理。 genericConverter不是null就将消息转化器强转一下再判断能否写出,否则直接判断能否写出(我们案例是这个)。其中参数valueType是返回对象类型,我们案例是valueType=Class<?> valueType = com.ours.www.framework.web.R.class。
在这里插入图片描述

@Override
 public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
  if (!canWrite(mediaType)) {// 调用父类AbstractHttpMessageConverter的canWrite方法
   return false;
  }
  if (mediaType != null && mediaType.getCharset() != null) {
   Charset charset = mediaType.getCharset();
   if (!ENCODINGS.containsKey(charset.name())) {
    return false;
   }
  }
  ObjectMapper objectMapper = selectObjectMapper(clazz, mediaType);
  if (objectMapper == null) {
   return false;
  }
  AtomicReference<Throwable> causeRef = new AtomicReference<>();
  if (objectMapper.canSerialize(clazz, causeRef)) {// 能够序列化,返回ture
   return true;
  }
  logWarningIfNecessary(clazz, causeRef.get());
  return false;
 }




 父类AbstractHttpMessageConverter的canWrite方法
 protected boolean canWrite(@Nullable MediaType mediaType) {
  if (mediaType == null || MediaType.ALL.equalsTypeAndSubtype(mediaType)) {// 这里的形参mediaType就是前面传过来的selectedMediaType,选中的媒体类型,如果前面的为null,这边也可以返回true,也就是说媒体类型为空,我们案例的MappingJackson2HttpMessageConverter也支持。
   return true;
  }
  for (MediaType supportedMediaType : getSupportedMediaTypes()) {// 循环得到自己能支持的所有媒体类型,每一个消息转化器都有自己能支持的媒体类型。我们案例的MappingJackson2HttpMessageConverter主要支持application/json 和 application/*+json这两种。
   if (supportedMediaType.isCompatibleWith(mediaType)) {// 是否适配传过来的,也就是选中的媒体类型,这边按照我们案例,刚好适配,返回ture
    return true;
   }
  }
  return false;
 }

MappingJackson2HttpMessageConverter是我们案例处理的消息转化器,可以将R对象和json数据互转,这边判断能否写出。
MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter extends AbstractHttpMessageConverter,canWrite方法在AbstractJackson2HttpMessageConverter父类里面,也间接调用了AbstractHttpMessageConverter里面的canWrite方法。
所有返回值类型都支持。
这边最终返回ture,【核心-canWrite分支线】执行完成,接下来看【核心-write分支线】。

我们来看【核心-write分支线】
在这里插入图片描述

@Override
 public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
   HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

  final HttpHeaders headers = outputMessage.getHeaders();// 获取文件头,我们案例为0个
  addDefaultHeaders(headers, t, contentType);// 添加默认响应头,例如Content-Type -> {ArrayList@16212}  size = 1   也就是Content-Type :application/json

  if (outputMessage instanceof StreamingHttpOutputMessage streamingOutputMessage) {// 我们案例没有进入
   streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() {
    @Override
    public OutputStream getBody() {
     return outputStream;
    }
    @Override
    public HttpHeaders getHeaders() {
     return headers;
    }
   }));
  }
  else {
   writeInternal(t, type, outputMessage);// 内部写入,调用子类AbstractJackson2HttpMessageConverter的writeInternal方法,基本是一个对象转json的流程。
   outputMessage.getBody().flush();
  }
 }
 
 
 
 子类AbstractJackson2HttpMessageConverter的writeInternal方法
 @Override
 protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
   throws IOException, HttpMessageNotWritableException {

  MediaType contentType = outputMessage.getHeaders().getContentType();
  JsonEncoding encoding = getJsonEncoding(contentType);

  Class<?> clazz = (object instanceof MappingJacksonValue mappingJacksonValue ?
    mappingJacksonValue.getValue().getClass() : object.getClass());
  ObjectMapper objectMapper = selectObjectMapper(clazz, contentType);
  Assert.state(objectMapper != null, () -> "No ObjectMapper for " + clazz.getName());

  OutputStream outputStream = StreamUtils.nonClosing(outputMessage.getBody());
  try (JsonGenerator generator = objectMapper.getFactory().createGenerator(outputStream, encoding)) {// generator生成器
   writePrefix(generator, object);

   Object value = object;
   Class<?> serializationView = null;
   FilterProvider filters = null;
   JavaType javaType = null;

   if (object instanceof MappingJacksonValue mappingJacksonValue) {
    value = mappingJacksonValue.getValue();
    serializationView = mappingJacksonValue.getSerializationView();
    filters = mappingJacksonValue.getFilters();
   }
   if (type != null && TypeUtils.isAssignable(type, value.getClass())) {
    javaType = getJavaType(type, null);
   }

   ObjectWriter objectWriter = (serializationView != null ?
     objectMapper.writerWithView(serializationView) : objectMapper.writer());// 拿到jackson的objectWriter
   if (filters != null) {
    objectWriter = objectWriter.with(filters);
   }
   if (javaType != null && (javaType.isContainerType() || javaType.isTypeOrSubTypeOf(Optional.class))) {
    objectWriter = objectWriter.forType(javaType);
   }
   SerializationConfig config = objectWriter.getConfig();
   if (contentType != null && contentType.isCompatibleWith(MediaType.TEXT_EVENT_STREAM) &&
     config.isEnabled(SerializationFeature.INDENT_OUTPUT)) {
    objectWriter = objectWriter.with(this.ssePrettyPrinter);
   }
   objectWriter = customizeWriter(objectWriter, javaType, contentType);
   objectWriter.writeValue(generator, value);// 【核心】对象转json了,并写给响应对象outputMessage(在outputMessage.servletResponse属性.response属性.outputBuffer属性.bb属性.hb里面,在idea中string形式展示hb就可以看到是我们案例的r对象的json形式了)

   writeSuffix(generator, object);
   generator.flush();
  }
  catch (InvalidDefinitionException ex) {
   throw new HttpMessageConversionException("Type definition error: " + ex.getType(), ex);
  }
  catch (JsonProcessingException ex) {
   throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
  }
 }

这边最终是一个对象转json数据了,并写给响应对象outputMessage,【核心-write分支线】执行完毕。

那么整个【核心-返回值处理分支线】执行完毕,也意味着【核心-执行处理分支线】执行完毕。该支线执行完毕,接下来就是【核心-获取ModelAndView对象分支线】
在这里插入图片描述

@Nullable
 protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
   HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

  ServletWebRequest webRequest = new ServletWebRequest(request, response);// 一些初始化操作
  WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
  ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

  ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// Servlet可执行处理器方法,将handlerMethod又封装了一次,封装为可执行方法invocableMethod 。
  if (this.argumentResolvers != null) {// 给invocableMethod设置参数解析器,确定将要执行的目标方法的每一个参数的具体值是什么。这个SpringMVC6.0.11版本有31个参数解析器。
   invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
  }
  if (this.returnValueHandlers != null) {// 给invocableMethod设置返回值处理器,确定将要执行的目标方法的返回值,这个SpringMVC6.0.11版本有有15个返回值处理器。
   invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
  }
  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);

  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);
  }

  invocableMethod.invokeAndHandle(webRequest, mavContainer);// 【核心-执行处理分支线】执行并处理,真正执行模板方法。返回的所有数据都放到了mavContainer对象中,这个是模型和视图容器对象,包含了要去的页面地址View和模块Model数据。
  if (asyncManager.isConcurrentHandlingStarted()) {
   return null;
  }

  return getModelAndView(mavContainer, modelFactory, webRequest);// 【核心-获取ModelAndView对象分支线】获取ModelAndView对象。
 }

我们来看【核心-获取ModelAndView对象分支线】
断点进入return getModelAndView(mavContainer, modelFactory, webRequest);
【核心-获取ModelAndView对象分支线】获取ModelAndView对象。
在这里插入图片描述

@Nullable
 private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
   ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {

  modelFactory.updateModel(webRequest, mavContainer);// 更新模块数据
  if (mavContainer.isRequestHandled()) {
   return null;
  }
  ModelMap model = mavContainer.getModel();// 获取模块值
  ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
  if (!mavContainer.isViewReference()) {
   mav.setView((View) mavContainer.getView());
  }
  if (model instanceof RedirectAttributes redirectAttributes) {// 重定向携带数据
   Map<String, ?> flashAttributes = redirectAttributes.getFlashAttributes();
   HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
   if (request != null) {
    RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);// 放到请求上下文中
   }
  }
  return mav;
 }

断点进入modelFactory.updateModel(webRequest, mavContainer);
在这里插入图片描述

public void updateModel(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
  ModelMap defaultModel = container.getDefaultModel();// 获取模块值
  if (container.getSessionStatus().isComplete()){
   this.sessionAttributesHandler.cleanupAttributes(request);
  }
  else {
   this.sessionAttributesHandler.storeAttributes(request, defaultModel);
  }
  if (!container.isRequestHandled() && container.getModel() == defaultModel) {
   updateBindingResult(request, defaultModel);// 更新最终绑定结果,有绑定策略就会绑定
  }
 }
 

 private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
  List<String> keyNames = new ArrayList<>(model.keySet());
  for (String name : keyNames) {
   Object value = model.get(name);
   if (value != null && isBindingCandidate(name, value)) {
    String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
    if (!model.containsAttribute(bindingResultKey)) {
     WebDataBinder dataBinder = this.dataBinderFactory.createBinder(request, value, name);
     model.put(bindingResultKey, dataBinder.getBindingResult());
    }
   }
  }
 }

绑定结果后,层层返回后,最终将mv返回去了。

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
【核心4】执行目标方法,这步会进入到Controller里面,执行具体方法的具体业务代码

铁哥小结
关键对象
HandlerMethodArgumentResolver:参数解析器,接口,SpringMVC6.0.11版本有31个核心实现类,设计思路和Adapter一样,先看是否支持这种参数解析,支持的话就解析。

HandlerMethodReturnValueHandler:返回值处理器,接口,SpringMVC6.0.11版本有15个核心实现类,设计思路和Adapter一样,先看是否支持这种返回值处理,支持的话就返回处理。

InvocableHandlerMethod:继承了HandlerMethod,该类中的invokeForRequest方法做了获取参数具体值和反射工具处理,真正进入了业务Controller里面,执行了业务方法代码。

ServletInvocableHandlerMethod:类,继承了InvocableHandlerMethod,将HandlerMethod进行封装,设置了参数解析器,设置了返回值处理器。该类中的invokeAndHandle方法中的Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);这行代码真正进入了业务Controller里面,执行了业务方法代码(本质是调用父类的invokeForRequest方法)。

MethodParameter:类,继承Object类,方法参数对象,是方法参数的各种详细封装,例如参数的注解、索引位置、参数类型等等

PathVariableMethodArgumentResolver:类,实现了HandlerMethodArgumentResolver,是31个核心实现类之一,方法参数前面标记有PathVariable注解并且注解有值,这类参数就用该解析器解析出参数的具体值。

PathVariableMapMethodArgumentResolver:类,实现了HandlerMethodArgumentResolver,是31个核心实现类之一,方法参数标记有PathVariable注解并且注解有值而且得是Map类型,这类参数就用该解析器解析出参数的具体值。

RequestParamMethodArgumentResolver:类,实现了HandlerMethodArgumentResolver,是31个核心实现类之一,方法参数标记有RequestParam注解和其他一些判断条件,文件上传下载也是这个参数解析器处理。

ServletModelAttributeMethodProcessor:类,实现了HandlerMethodArgumentResolver,是31个核心实现类之一,方法参数是自定义对象,对象前不加任何注解,这类参数就用该解析器解析出参数的具体值。Get请求下,业务开发中最常用的参数解析器。

ModelAndViewContainer:类,继承Object类,模型和视图容器对象,包含了要去的页面地址View和模块Model数据。

ModelAndViewMethodReturnValueHandler:类,实现了HandlerMethodReturnValueHandler,是15个核心实现类之一,返回值类型是ModelAndView.class,这类返回值就用该返回值处理器处理返回。

RequestResponseBodyMethodProcessor:类,实现了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler两个接口,既是返回值处理器又是参数解析器,是业务开发,尤其是现在前后端分离的情况下,最常用的返回值处理器。在Post请求下参数前面标记了@RequestBody注解,就用该参数解析器解析出参数的具体值。返回值标记了@ResponseBody注解的时候,就用该返回值处理器处理返回。

HttpMessageConverter:接口,是消息转化器接口,SpringMVC6.0.11版本有9个默认核心实现类,有9个消息转化器,该接口主要功能就是看是否支持将此Class类型的对象,转化为MediaType类型的数据。例如我们案例这种,是否能将返回值R.class转为json数据,而且也能将json数据转为R.class。

GenericHttpMessageConverter:接口,继承HttpMessageConverter,比父接口少了几个方法,有个特别优秀的后代MappingJackson2HttpMessageConverter,这个后代是业务开发中最常用的消息转化器,该后代支持返回值对象和json数据互转。

AbstractHttpMessageConverter:抽象类,实现HttpMessageConverter接口,是部分消息转化器的父类,有canWrite和canRead两个王炸方法,在源码中作判断,判断消息转化器是否能够写出和读入。

MappingJackson2HttpMessageConverter:类,继承AbstractHttpMessageConverter实现了HttpMessageConverter,是9个核心实现类之一,调用父类AbstractHttpMessageConverter的canWrite方法和自己的canWrite方法在源码中作判断。是业务开发中最常用的消息转化器,支持返回值对象和json数据互转。扩展:SpringMVC依赖默认引入了Jackson相关jar包的支持,所以才有该消息转化器的支持。

难点:【核心2】选中的大型反射工具Adapter,在这边执行处理操作,本质就是反射操作。参数解析器解析出所有参数的具体值,返回值处理器通过消息转化器将业务Controller里面的方法返回值返回处理。一般都是RequestResponseBodyMethodProcessor这个返回值处理器去处理的,然后根据服务器支持返回的类型(比如json等)和发出请求客户端支持的类型(比如xml、*/* 等)求交集决定(准确的说应该是最佳匹配内容协商),是转为json,还是转为xml,或者转为pdf等等。例如,我们可以引入支持转为xml的jar包,服务器就有了能返回xml的能力,服务器支持返回的类型就有json和xml,用浏览器请求,由于浏览器请求头的Accept属性中xml优先级高于*/*,所以求交集以后是xml,浏览器得到的返回值展示为xml,而postman或者apifox之类的Accept属性中默认不修改的话只有*/*一项,求交集以后是json和xml,源码中循环判断取第一个后就break了,由于json在前面先被循环到了,所以得到的返回值展示为json。如果将postman或者apifox默认的Accept属性的*/*改为application/xml,和服务器支持类型求交集以后就是xml,那么得到的返回值展示为xml。

内容协商(消息转化器的应用)技术扩展点
1、在yml中配置开启参数方式的内容协商:

spring:
  mvc:
    contentnegotiation:
      favor-parameter: true # 开启参数方式的内容协商

2、在pom.xml引入支持xml返回的jar包,一般也是jackson-xml的相关jar包。
3、浏览器发送下面请求,参数不同,返回方式不同:
http://localhost:8888/user/detailOrgPerson/123?format=json 浏览器就返回json格式数据。
http://localhost:8888/user/detailOrgPerson/123?format=xml 浏览器就返回xml格式数据。

6、核心5源码分析

mappedHandler.applyPostHandle(processedRequest, response, mv);/
【核心5】拦截器-方法正常返回后

7、核心6源码分析

this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
【核心6】处理派发结果

8、核心7源码分析

this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
【核心7】拦截器-方法是否抛异常都会执行

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝影铁哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值