目录
传送门
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】拦截器-方法是否抛异常都会执行