mirror of
https://github.com/youthlql/JavaYouth.git
synced 2026-04-13 03:45:19 +00:00
1359 lines
51 KiB
Markdown
1359 lines
51 KiB
Markdown
# 第10章-SpringMVC中的HandlerAdapter源码解析
|
||
|
||
## 测试类
|
||
|
||
### HelloController
|
||
|
||
```java
|
||
package cn.imlql.web.controller;
|
||
|
||
@Controller
|
||
public class HelloController {
|
||
|
||
public HelloController() {
|
||
System.out.println("HelloController.....");
|
||
}
|
||
|
||
@Autowired
|
||
HelloService helloService;
|
||
|
||
@GetMapping("/hello")
|
||
public String sayHello(String name,
|
||
@RequestParam("user") String user,
|
||
HttpSession session) {
|
||
String mvc = helloService.say(user + ":MVC" + name);
|
||
session.setAttribute("msg", mvc);
|
||
return "index.jsp";
|
||
}
|
||
}
|
||
|
||
```
|
||
|
||
### index.jsp
|
||
|
||
```jsp
|
||
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
|
||
<html>
|
||
<head>
|
||
<title>$Title$</title>
|
||
</head>
|
||
<body>
|
||
$END$
|
||
结果: <h1 style="color: red">${sessionScope.msg}</h1>
|
||
</body>
|
||
</html>
|
||
```
|
||
|
||
其余的类和前面一样,不列举了。
|
||
|
||
## BeanNameUrlHandlerMapping简介
|
||
|
||
```java
|
||
public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
|
||
|
||
/**
|
||
* Checks name and aliases of the given bean for URLs, starting with "/".
|
||
*/
|
||
@Override //决定使用哪个url注册
|
||
protected String[] determineUrlsForHandler(String beanName) {
|
||
List<String> urls = new ArrayList<>();
|
||
if (beanName.startsWith("/")) {
|
||
urls.add(beanName);
|
||
}
|
||
String[] aliases = obtainApplicationContext().getAliases(beanName);
|
||
for (String alias : aliases) {
|
||
if (alias.startsWith("/")) {
|
||
urls.add(alias);
|
||
}
|
||
}
|
||
return StringUtils.toStringArray(urls);
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
1. 很简单,意思就是只要你的BeanName是以/开头,就会将这个Bean封装成一个BeanNameUrlHandlerMapping映射。
|
||
2. 具体路径怎么写呢?你可以在你的类上写一个`@Controller("/helloReq")` ,这样就有路径了。代表的意思就是`/helloReq`这个URL和由哪个handler来处理的映射关系被保存在了BeanNameUrlHandlerMapping里
|
||
3. 如果不懂的话可以待会看下面的例子
|
||
|
||
|
||
|
||
> handler就是咱们常说的XXXController
|
||
|
||
|
||
|
||
## HandlerAdapter概述
|
||
|
||
1. 上面我们从HandlerMapping这个映射中心找到了由哪个Controller执行哪个方法
|
||
2. 按照我们最简单的想法就是直接method.invoke()反射执行
|
||
3. 但是实际上我们要考虑
|
||
1. 是哪个对象执行方法?
|
||
2. 方法里的参数有几个?
|
||
3. 参数类型是什么?参数类型大概率有我们自己写的类,怎么处理
|
||
4. 多个参数我们还要一个一个赋值。
|
||
5. 怎么返回?是直接返回值,还是跳转页面,等等
|
||
4. 其实是比较复杂的,springmvc就写了一个HandlerAdapter来处理这些复杂的逻辑
|
||
|
||
还是跟HandlerMapping一样,继续Debug `DispatcherServlet#doDispatch(HttpServletRequest, HttpServletResponse)`
|
||
|
||
|
||
|
||
浏览器输入:http://localhost:8080/springmvc_source_test/hello?name=zhangsan&user=haha开始测试
|
||
|
||
### DispatcherServlet#doDispatch()请求派发
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019110419677.png" />
|
||
|
||
|
||
|
||
```java
|
||
//SpringMVC处理请求的核心流程
|
||
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||
HttpServletRequest processedRequest = request;
|
||
HandlerExecutionChain mappedHandler = null; //handler(目标方法)的执行链
|
||
boolean multipartRequestParsed = false; //文件上传标志
|
||
//对异步请求的支持(Servlet3.0以后才有的,Webflux)
|
||
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
|
||
|
||
try {
|
||
ModelAndView mv = null;
|
||
Exception dispatchException = null;
|
||
|
||
try {
|
||
processedRequest = checkMultipart(request); //检查当前是否文件上传请求
|
||
multipartRequestParsed = (processedRequest != request);
|
||
|
||
//构造出了【目标方法+拦截器整个链路】决定使用哪个Handler处理当前请求 Determine handler for the current request.
|
||
mappedHandler = getHandler(processedRequest);
|
||
if (mappedHandler == null) { //如果找不到人处理,就send 404
|
||
noHandlerFound(processedRequest, response);
|
||
return;
|
||
}
|
||
|
||
//适配器怎么找的、 Determine handler adapter for the current request.
|
||
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
|
||
|
||
// Process last-modified header, if supported by the handler.
|
||
String method = request.getMethod();
|
||
boolean isGet = "GET".equals(method);
|
||
if (isGet || "HEAD".equals(method)) {
|
||
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
|
||
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
|
||
return;
|
||
}
|
||
}
|
||
//所有拦截器的 preHandle 执行
|
||
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
|
||
return; //使用 mappedHandler整个链
|
||
}
|
||
|
||
//真正来执行目标方法 Actually invoke the handler.
|
||
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
|
||
|
||
if (asyncManager.isConcurrentHandlingStarted()) {
|
||
return;
|
||
}
|
||
|
||
applyDefaultViewName(processedRequest, mv);
|
||
mappedHandler.applyPostHandle(processedRequest, response, mv);
|
||
}
|
||
catch (Exception ex) {
|
||
dispatchException = ex;
|
||
}
|
||
catch (Throwable err) {
|
||
// As of 4.3, we're processing Errors thrown from handler methods as well,
|
||
// making them available for @ExceptionHandler methods and other scenarios.
|
||
dispatchException = new NestedServletException("Handler dispatch failed", err);
|
||
} //处理结果
|
||
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
|
||
}
|
||
catch (Exception ex) {
|
||
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
|
||
}
|
||
catch (Throwable err) {
|
||
triggerAfterCompletion(processedRequest, response, mappedHandler,
|
||
new NestedServletException("Handler processing failed", err));
|
||
}
|
||
finally {
|
||
if (asyncManager.isConcurrentHandlingStarted()) {
|
||
// Instead of postHandle and afterCompletion
|
||
if (mappedHandler != null) {
|
||
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
|
||
}
|
||
}
|
||
else {
|
||
// Clean up any resources used by a multipart request.
|
||
if (multipartRequestParsed) {
|
||
cleanupMultipart(processedRequest);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
F7进入`getHandlerAdapter(mappedHandler.getHandler())`
|
||
|
||
|
||
|
||
|
||
|
||
### DispatcherServlet#getHandlerAdapter()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019111247967.png" />
|
||
|
||
上面几个默认的适配器还是在`DispatcherServlet.properties`配置的4个默认适配器,然后在初始化九大组件的时候放到容器里
|
||
|
||
|
||
|
||
### HttpRequestHandlerAdapter#supports()
|
||
|
||
```java
|
||
@Override //想让他工作,实现HttpRequestHandler接口即可
|
||
public boolean supports(Object handler) { //写一个HttpRequestHandler的实现也能处理请求
|
||
return (handler instanceof HttpRequestHandler);
|
||
}
|
||
```
|
||
|
||
这里判断当前handler【就是咱们写的Controller】是否实现了HttpRequestHandler接口,是的话就直接返回这个Adapter,不往下走了
|
||
|
||
### SimpleControllerHandlerAdapter#supports()
|
||
|
||
```java
|
||
@Override
|
||
public boolean supports(Object handler) {
|
||
return (handler instanceof Controller);
|
||
}
|
||
```
|
||
|
||
同理,这里判断当前handler是否实现了Controller接口,是的话就直接返回这个Adapter,不往下走了
|
||
|
||
### RequestMappingHandlerAdapter
|
||
|
||
RequestMappingHandlerAdapter自己没有写`supports`方法,它用的是父类的AbstractHandlerMethodAdapter的
|
||
|
||
#### AbstractHandlerMethodAdapter#supports()
|
||
|
||
```java
|
||
public final boolean supports(Object handler) {
|
||
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
|
||
}
|
||
```
|
||
|
||
这个就是判断当前handler是不是HandlerMethod类型的,前面大致讲过只要标注了@RequestMapping注解的handler最终都会被封装成HandlerMethod
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019194635523.png" />
|
||
|
||
#### RequestMappingHandlerAdapter#supportsInternal()
|
||
|
||
```java
|
||
protected boolean supportsInternal(HandlerMethod handlerMethod) {
|
||
return true;
|
||
}
|
||
```
|
||
|
||
### DispatcherServlet#doDispatch()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019194742173.png"/>
|
||
|
||
## 举例说明HttpRequestHandlerAdapter和SimpleControllerHandlerAdapter如何与BeanNameUrlHandlerMapping联动
|
||
|
||
### 测试类-HelloHttpRequestHandler
|
||
|
||
```java
|
||
@Controller("/helloReq") //BeanNameUrlHandlerMapping 就会把他注册进去
|
||
public class HelloHttpRequestHandler implements HttpRequestHandler {
|
||
//启用 HttpRequestHandlerAdapter
|
||
|
||
//处理请求
|
||
@Override
|
||
public void handleRequest(HttpServletRequest request,
|
||
HttpServletResponse response) throws ServletException, IOException {
|
||
response.getWriter().write("HelloHttpRequestHandler....");
|
||
}
|
||
}
|
||
```
|
||
|
||
`@Controller("/helloReq")`里的/helloReq即是BeanName也是URL。这里就是之前说的BeanName以/为开头的Bean,它的URL和handler的映射关系会被保存在BeanNameUrlHandlerMapping里,如下图
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019200240789.png" />
|
||
|
||
|
||
|
||
### DispatcherServlet#getHandlerAdapter()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019195313996.png" />
|
||
|
||
因为我们的HelloHttpRequestHandler正好实现了HttpRequestHandler,所以就会直接返回HttpRequestHandlerAdapter
|
||
|
||
|
||
|
||
|
||
|
||
### DispatcherServlet#doDispatch()
|
||
|
||
我们直接来到准备执行的地方
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019195555663.png" />
|
||
|
||
F7进入`ha.handle(processedRequest, response, mappedHandler.getHandler())`
|
||
|
||
### HttpRequestHandlerAdapter#handle()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211019195838240.png" />
|
||
|
||
直接来到了这里,接着就调用我们自定义的HelloHttpRequestHandler的handleRequest方法。
|
||
|
||
|
||
|
||
|
||
|
||
### 测试类-HelloSimpleController
|
||
|
||
```java
|
||
//@RequestMapping("/ssss")
|
||
@Controller("/helloSimple")
|
||
public class HelloSimpleController implements Controller {
|
||
@Override
|
||
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||
return null;
|
||
}
|
||
}
|
||
```
|
||
|
||
实现Controller接口的就是SimpleControllerHandlerAdapter来处理,原理和HttpRequestHandlerAdapter几乎一样。
|
||
|
||
|
||
|
||
|
||
|
||
### 小总结
|
||
|
||
1. 很显然BeanNameUrlHandlerMapping和HttpRequestHandlerAdapter的结合,一个类只能处理一个URL路径的请求。并且这里的参数处理都很麻烦,不像@RequestMapping那么方便
|
||
2. 远不如RequestMappingHandlerMapping和RequestMappingHandlerAdapter的结合来的方便。它两结合后,一个类里可以写多个@RequestMapping注解标注的方法,一个类里就可以处理多个URL请求,并且处理参数和返回值都很方便。
|
||
3. 所以下面就重点讲RequestMappingHandlerAdapter
|
||
|
||
|
||
|
||
|
||
|
||
## RequestMappingHandlerAdapter中的参数解析器、返回值处理器=>概述
|
||
|
||
### DispatcherServlet#doDispatch()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020111632694.png" />
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
### AbstractHandlerMethodAdapter#handle()
|
||
|
||
```java
|
||
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||
throws Exception {
|
||
|
||
return handleInternal(request, response, (HandlerMethod) handler);
|
||
}
|
||
```
|
||
|
||
|
||
|
||
AbstractHandlerMethodAdapter是RequestMappingHandlerAdapter的父类,之前说过
|
||
|
||
|
||
|
||
### RequestMappingHandlerAdapter#handleInternal()
|
||
|
||
```java
|
||
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);
|
||
}
|
||
|
||
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
|
||
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
|
||
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
|
||
}
|
||
else {
|
||
prepareResponse(response);
|
||
}
|
||
}
|
||
|
||
return mav;
|
||
}
|
||
|
||
//session锁这个东西不适用于高并发场景,所以spring默认是不适用它,应该是可以开启的,具体怎么开启我没研究,可以看官方文档
|
||
private boolean synchronizeOnSession = false;
|
||
```
|
||
|
||
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020112509616.png"/>
|
||
|
||
|
||
|
||
|
||
|
||
### RequestMappingHandlerAdapter#invokeHandlerMethod( )
|
||
|
||
```java
|
||
/**
|
||
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
|
||
* if view resolution is required.
|
||
* @since 4.2
|
||
* @see #createInvocableHandlerMethod(HandlerMethod)
|
||
*/
|
||
@Nullable
|
||
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
|
||
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
|
||
//把原生的request,response封装到一个对象中方便后续只用这一个参数就行【装饰器模式】
|
||
ServletWebRequest webRequest = new ServletWebRequest(request, response);
|
||
try { //数据绑定器
|
||
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
|
||
//获取到模型工厂 Model(要交给页面的数据) View(我们要去的 视图),视图可以理解为页面或者图片等等
|
||
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
|
||
|
||
// 这里就是做了一些增强,比如我们可以直接通过ServletInvocableHandlerMethod拿到返回值,可以很方便的拿到其它我们需要的信息
|
||
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
|
||
if (this.argumentResolvers != null) { //参数解析器
|
||
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
|
||
}
|
||
if (this.returnValueHandlers != null) { //返回值解析器
|
||
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
|
||
}
|
||
invocableMethod.setDataBinderFactory(binderFactory);
|
||
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
|
||
//以上的 几个核心组件都挺重要的 ModelAndViewContainer是以后处理过程中产生的ModelAndView数据临时存储容器
|
||
// 也是整个请求处理流程,线程共享数据
|
||
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);
|
||
if (asyncManager.isConcurrentHandlingStarted()) {
|
||
return null;
|
||
}
|
||
|
||
return getModelAndView(mavContainer, modelFactory, webRequest);
|
||
}
|
||
finally {
|
||
webRequest.requestCompleted();
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 什么是WebDataBinderFactory?
|
||
|
||
```java
|
||
public String sayHello(Person person){
|
||
//...
|
||
}
|
||
```
|
||
|
||
1. 假设方法里有一个Person类型的参数,Person里的属性就是age,name,sex这些的
|
||
2. 前端传的参数名刚好能和Person的属性对上,WebDataBinder就会帮我们自动绑定上。
|
||
|
||
#### argumentResolvers参数解析器
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020143347865.png" />
|
||
|
||
|
||
|
||
注意有一些解析器,比如RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器
|
||
|
||
#### returnValueHandlers返回值解析器
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020143311546.png" />
|
||
|
||
|
||
|
||
### 15个参数解析器和27个返回值解析器是什么时候有值的?
|
||
|
||
```java
|
||
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
|
||
implements BeanFactoryAware, InitializingBean {
|
||
// ...
|
||
}
|
||
```
|
||
|
||
|
||
|
||
我们可以看到RequestMappingHandlerAdapter也是实现了InitializingBean接口,所以它什么时候有值,和RequestMappingHandlerMapping是一样的逻辑
|
||
|
||
1. `DispatcherServlet#onRefresh()`开始初始化九大组件,就会开始初始化HandlerAdapter
|
||
2. 首先是创建`DispatcherServlet.properties`里指定的四个HandlerAdapter实现类的对象。还是用createBean来创建HandlerAdapter的
|
||
3. 其中RequestMappingHandlerAdapter创建完对象后,因为它实现了InitializingBean,所以会调用RequestMappingHandlerAdapter#afterPropertiesSet()
|
||
4. 在afterPropertiesSet()中就直接new了所有默认的参数解析器和返回值解析器
|
||
|
||
|
||
|
||
#### RequestMappingHandlerAdapter#afterPropertiesSet()
|
||
|
||
```java
|
||
public void afterPropertiesSet() { //初始化以后
|
||
// Do this first, it may add ResponseBody advice beans
|
||
initControllerAdviceCache(); //初始化 ControllerAdvice 【异常处理相关的功能】
|
||
|
||
if (this.argumentResolvers == null) { //拿到底层所有的默认 argumentResolvers
|
||
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
|
||
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); //把这些resolver统一组合到一个对象里面,方便管控
|
||
}
|
||
if (this.initBinderArgumentResolvers == null) {
|
||
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
|
||
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
|
||
}
|
||
if (this.returnValueHandlers == null) {
|
||
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
|
||
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
|
||
|
||
```java
|
||
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
|
||
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
|
||
|
||
// Annotation-based argument resolution
|
||
//这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲
|
||
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
|
||
resolvers.add(new RequestParamMapMethodArgumentResolver());
|
||
resolvers.add(new PathVariableMethodArgumentResolver());
|
||
resolvers.add(new PathVariableMapMethodArgumentResolver());
|
||
resolvers.add(new MatrixVariableMethodArgumentResolver());
|
||
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
|
||
resolvers.add(new ServletModelAttributeMethodProcessor(false));
|
||
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
|
||
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
|
||
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
|
||
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
|
||
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
|
||
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
|
||
resolvers.add(new SessionAttributeMethodArgumentResolver());
|
||
resolvers.add(new RequestAttributeMethodArgumentResolver());
|
||
|
||
// Type-based argument resolution
|
||
resolvers.add(new ServletRequestMethodArgumentResolver());
|
||
resolvers.add(new ServletResponseMethodArgumentResolver());
|
||
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
|
||
resolvers.add(new RedirectAttributesMethodArgumentResolver());
|
||
resolvers.add(new ModelMethodProcessor());
|
||
resolvers.add(new MapMethodProcessor());
|
||
resolvers.add(new ErrorsMethodArgumentResolver());
|
||
resolvers.add(new SessionStatusMethodArgumentResolver());
|
||
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
|
||
if (KotlinDetector.isKotlinPresent()) {
|
||
resolvers.add(new ContinuationHandlerMethodArgumentResolver());
|
||
}
|
||
|
||
// Custom arguments
|
||
if (getCustomArgumentResolvers() != null) {
|
||
resolvers.addAll(getCustomArgumentResolvers());
|
||
}
|
||
|
||
// Catch-all
|
||
resolvers.add(new PrincipalMethodArgumentResolver());
|
||
//这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true
|
||
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
|
||
resolvers.add(new ServletModelAttributeMethodProcessor(true));
|
||
|
||
return resolvers;
|
||
}
|
||
```
|
||
|
||
#### RequestMappingHandlerAdapter#getDefaultReturnValueHandlers()
|
||
|
||
```java
|
||
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
|
||
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
|
||
|
||
// Single-purpose return value types
|
||
handlers.add(new ModelAndViewMethodReturnValueHandler());
|
||
handlers.add(new ModelMethodProcessor());
|
||
handlers.add(new ViewMethodReturnValueHandler());
|
||
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
|
||
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
|
||
handlers.add(new StreamingResponseBodyReturnValueHandler());
|
||
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
|
||
this.contentNegotiationManager, this.requestResponseBodyAdvice));
|
||
handlers.add(new HttpHeadersReturnValueHandler());
|
||
handlers.add(new CallableMethodReturnValueHandler());
|
||
handlers.add(new DeferredResultMethodReturnValueHandler());
|
||
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
|
||
|
||
// Annotation-based return value types
|
||
handlers.add(new ServletModelAttributeMethodProcessor(false));
|
||
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
|
||
this.contentNegotiationManager, this.requestResponseBodyAdvice));
|
||
|
||
// Multi-purpose return value types
|
||
handlers.add(new ViewNameMethodReturnValueHandler());
|
||
handlers.add(new MapMethodProcessor());
|
||
|
||
// Custom return value types
|
||
if (getCustomReturnValueHandlers() != null) {
|
||
handlers.addAll(getCustomReturnValueHandlers());
|
||
}
|
||
|
||
// Catch-all
|
||
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
|
||
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
|
||
}
|
||
else {
|
||
handlers.add(new ServletModelAttributeMethodProcessor(true));
|
||
}
|
||
|
||
return handlers;
|
||
}
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
## RequestMappingHandlerAdapter中的参数解析器工作流程
|
||
|
||
### DispatcherServlet#doDispatch()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020153627726.png" />
|
||
|
||
|
||
|
||
`AbstractHandlerMethodAdapter#handle(HttpServletRequest , HttpServletResponse , Object )`和`RequestMappingHandlerAdapter#handleInternal(HttpServletRequest , HttpServletResponse , HandlerMethod )`上面说过,跳过
|
||
|
||
### RequestMappingHandlerAdapter#invokeHandlerMethod()准备执行目标方法
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020153939442.png" />
|
||
|
||
|
||
|
||
|
||
|
||
### ServletInvocableHandlerMethod#invokeAndHandle()真正开始执行目标方法
|
||
|
||
```java
|
||
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
|
||
Object... providedArgs) throws Exception {
|
||
//目标方法的反射执行过程
|
||
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
|
||
setResponseStatus(webRequest);
|
||
|
||
if (returnValue == null) {
|
||
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
|
||
disableContentCachingIfNecessary(webRequest);
|
||
mavContainer.setRequestHandled(true);
|
||
return;
|
||
}
|
||
}
|
||
else if (StringUtils.hasText(getResponseStatusReason())) {
|
||
mavContainer.setRequestHandled(true);
|
||
return;
|
||
}
|
||
|
||
mavContainer.setRequestHandled(false);
|
||
Assert.state(this.returnValueHandlers != null, "No return value handlers");
|
||
try {
|
||
this.returnValueHandlers.handleReturnValue(
|
||
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
|
||
}
|
||
catch (Exception ex) {
|
||
if (logger.isTraceEnabled()) {
|
||
logger.trace(formatErrorForReturnValue(returnValue), ex);
|
||
}
|
||
throw ex;
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### InvocableHandlerMethod#invokeForRequest( )目标方法的反射执行过程
|
||
|
||
```java
|
||
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); //就是反射执行
|
||
}
|
||
```
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020154445639.png" />
|
||
|
||
开始进入正题
|
||
|
||
### InvocableHandlerMethod#getMethodArgumentValues()获取方法的请求参数
|
||
|
||
```java
|
||
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
|
||
Object... providedArgs) throws Exception {
|
||
//直接拿到方法的所有参数,getMethodParameters是HandlerMethod类的,在之前构造handler的时候就已经通过反射将相关信息保存好了
|
||
// 我们之前讲过标注@RequestMapping注解的handler最终会被封装成HandlerMethod
|
||
MethodParameter[] parameters = getMethodParameters();
|
||
if (ObjectUtils.isEmpty(parameters)) {
|
||
return EMPTY_ARGS;
|
||
}
|
||
//准备args数组(和parameters一样长),挨个确定每个参数都是什么值
|
||
Object[] args = new Object[parameters.length];
|
||
for (int i = 0; i < parameters.length; i++) {
|
||
MethodParameter parameter = parameters[i];
|
||
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||
args[i] = findProvidedArgument(parameter, providedArgs); //先去已提供的参数中找
|
||
if (args[i] != null) {
|
||
continue;
|
||
}
|
||
if (!this.resolvers.supportsParameter(parameter)) { //支持这种参数的解析器也会被放到缓存,下一次进来,就不用27个人挨个判断
|
||
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;
|
||
}
|
||
```
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020160234158.png" />
|
||
|
||
### HandlerMethodArgumentResolverComposite准备循环27个参数解析器
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020160936355.png" />
|
||
|
||
### HandlerMethodArgumentResolverComposite#supportsParameter()
|
||
|
||
```java
|
||
public boolean supportsParameter(MethodParameter parameter) {
|
||
return getArgumentResolver(parameter) != null;
|
||
}
|
||
```
|
||
|
||
### HandlerMethodArgumentResolverComposite#getArgumentResolver()循环判断哪个参数解析器支持这个参数
|
||
|
||
```java
|
||
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
|
||
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); //先看缓存中有没有
|
||
if (result == null) {
|
||
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
|
||
if (resolver.supportsParameter(parameter)) {
|
||
result = resolver;
|
||
this.argumentResolverCache.put(parameter, result);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
```
|
||
|
||
这里就是循环27个参数解析器,看哪一个能解析这种类型的参数。因为参数解析器太多,我们就举几个常用的例子
|
||
|
||
|
||
|
||
### RequestParamMethodArgumentResolver判断@RequestParam注解的参数解析器
|
||
|
||
```java
|
||
package org.springframework.web.method.annotation
|
||
|
||
|
||
public boolean supportsParameter(MethodParameter parameter) {
|
||
if (parameter.hasParameterAnnotation(RequestParam.class)) {
|
||
//注意这里是为了不跟下面的RequestParamMapMethodArgumentResolver产生冲突
|
||
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
|
||
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
|
||
return (requestParam != null && StringUtils.hasText(requestParam.name()));
|
||
}
|
||
else {
|
||
return true;
|
||
}
|
||
}
|
||
else {
|
||
if (parameter.hasParameterAnnotation(RequestPart.class)) {
|
||
return false;
|
||
}
|
||
parameter = parameter.nestedIfOptional();
|
||
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
|
||
return true;
|
||
}
|
||
else if (this.useDefaultResolution) {
|
||
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
|
||
}
|
||
else {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
这个就是判断参数有没有标@RequestParam注解
|
||
|
||
注意这个是org.springframework.web.method.annotation包下的
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020164639063.png"/>
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020164438763.png" />
|
||
|
||
1. 第一次进入`RequestParamMethodArgumentResolver#supportsParameter(MethodParameter parameter)`时,我们在前面讲过,在`RequestMappingHandlerAdapter#getDefaultArgumentResolvers()`添加默认解析器的时候,spring往参数解析器里加了两个
|
||
|
||
```java
|
||
//这里放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是false,第二个参数是什么呢?我们后面说它的时候再讲
|
||
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
|
||
|
||
//这里也放了一个RequestParamMethodArgumentResolver,但是它的第二个参数是true
|
||
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
|
||
```
|
||
|
||
2. 这里的true和false指定的就是useDefaultResolution这个东西
|
||
|
||
3. 然后我们验证一下第二个RequestParamMethodArgumentResolver
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020165119933.png" />
|
||
|
||
4. useDefaultResolution这个看名字应该就是前面25个处理器都用不到的时候,就用这个默认中的默认处理器第26个`RequestParamMethodArgumentResolver`(双重默认)
|
||
5. 那什么时候用第27个`ServletModelAttributeMethodProcessor`这个处理器呢?当第26个在上面那几个if else中返回false,就会用最后这个,至于什么时候返回false,我没有深入研究。
|
||
|
||
|
||
|
||
### RequestParamMapMethodArgumentResolver参数解析器
|
||
|
||
> `@RequestParam Map<String,Object> params`
|
||
|
||
```java
|
||
public boolean supportsParameter(MethodParameter parameter) {
|
||
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
|
||
return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
|
||
!StringUtils.hasText(requestParam.name()));
|
||
}
|
||
```
|
||
|
||
判断有没有标@RequestParam注解,并且类型是Map
|
||
|
||
|
||
|
||
### PathVariableMethodArgumentResolver判断@PathVariable注解的参数解析器
|
||
|
||
```java
|
||
public boolean supportsParameter(MethodParameter parameter) {
|
||
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
|
||
return false;
|
||
}
|
||
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
|
||
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
|
||
return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
|
||
}
|
||
return true;
|
||
}
|
||
```
|
||
|
||
相信大家已经能猜到了,这里就是判断参数上有没有标注@PathVariable注解
|
||
|
||
### 返回到InvocableHandlerMethod#getMethodArgumentValues( )
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020170650734.png" />
|
||
|
||
|
||
|
||
具体的参数赋值过程,就是一些数据类型的转换,可以自己去看下。
|
||
|
||
```
|
||
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
### 返回到InvocableHandlerMethod#invokeForRequest()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020171415141.png" />
|
||
|
||
接下来就是交给反射去执行
|
||
|
||
|
||
|
||
|
||
|
||
### 返回到ServletInvocableHandlerMethod#invokeAndHandle()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020171844159.png" />
|
||
|
||
拿到返回值了,接下来交给返回值处理器
|
||
|
||
### SpringMVC到底能写哪些参数?
|
||
|
||
详见[官方文档-参数相关](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments)
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
## RequestMappingHandlerAdapter中的返回值解析器工作流程
|
||
|
||
```java
|
||
@Controller
|
||
public class HelloController {
|
||
|
||
public HelloController() {
|
||
System.out.println("HelloController.....");
|
||
}
|
||
|
||
@Autowired
|
||
HelloService helloService;
|
||
|
||
@GetMapping("/hello")
|
||
public String sayHello(String name,
|
||
@RequestParam("user") String user,
|
||
HttpSession session, HttpServletRequest request, //原生的session对象
|
||
@RequestHeader("User-Agent") String ua) { //@RequestParam Map<String,Object> params:所有请求参数全封装进来
|
||
// @RequestHeader("User-Agent") String ua 获取指定请求头的值
|
||
String header = request.getHeader("User-Agent");
|
||
//方法的签名,到底能写那些?
|
||
//详细参照 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-arguments
|
||
//https://www.bilibili.com/video/BV19K4y1L7MT?p=32
|
||
String mvc = helloService.say(user + ":MVC" + name);
|
||
session.setAttribute("msg", mvc);
|
||
|
||
|
||
//SpringMVC的目标方法能写哪些返回值
|
||
//https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
|
||
return "index.jsp";
|
||
}
|
||
}
|
||
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
### ServletInvocableHandlerMethod#invokeAndHandle()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020175522276.png"/>
|
||
|
||
我们可以看到RequestResponseBodyMethodProcessor比ViewNameMethodReturnValueHandler优先级高,接下来我们细讲
|
||
|
||
### HandlerMethodReturnValueHandlerComposite#handleReturnValue()
|
||
|
||
```java
|
||
@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);
|
||
}
|
||
```
|
||
|
||
### HandlerMethodReturnValueHandlerComposite#selectHandler()循环15个返回值处理器找到合适的返回值处理器
|
||
|
||
```java
|
||
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
|
||
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
|
||
//还是老样子for循环
|
||
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
|
||
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
|
||
continue;
|
||
}
|
||
if (handler.supportsReturnType(returnType)) {
|
||
return handler;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
```
|
||
|
||
我们就直接放行,看最终找到的是哪个
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020180845184.png"/>
|
||
|
||
|
||
|
||
### ViewNameMethodReturnValueHandler#supportsReturnType()
|
||
|
||
```java
|
||
public boolean supportsReturnType(MethodParameter returnType) {
|
||
Class<?> paramType = returnType.getParameterType();
|
||
//返回值是void,或者字符串
|
||
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### 返回到HandlerMethodReturnValueHandlerComposite#handleReturnValue()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020181114886.png" />
|
||
|
||
|
||
|
||
### ViewNameMethodReturnValueHandler#handleReturnValue()开始处理返回值
|
||
|
||
```java
|
||
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
|
||
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
|
||
|
||
if (returnValue instanceof CharSequence) { //只要是字符串就是跳转到的页面地址
|
||
String viewName = returnValue.toString();
|
||
mavContainer.setViewName(viewName);
|
||
if (isRedirectViewName(viewName)) { //是否是重定向的方式 redirect:
|
||
mavContainer.setRedirectModelScenario(true);
|
||
}
|
||
}
|
||
else if (returnValue != null) {
|
||
// should not happen
|
||
throw new UnsupportedOperationException("Unexpected return type: " +
|
||
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
|
||
}
|
||
}
|
||
```
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020181707508.png" />
|
||
|
||
然后就准备返回了
|
||
|
||
|
||
|
||
### 返回到RequestMappingHandlerAdapter#invokeHandlerMethod()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020182700550.png"/>
|
||
|
||
### RequestMappingHandlerAdapter#getModelAndView()进行视图解析相关工作
|
||
|
||
```java
|
||
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
|
||
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
|
||
//modelFactory准备模型数据 (请求域数据共享)session里面的数据搬家到request域
|
||
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) {
|
||
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
|
||
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
|
||
if (request != null) { //重定向数据的共享,RedirectView。先把数据移到request,再把request移到session
|
||
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
|
||
}
|
||
}
|
||
return mav;
|
||
}
|
||
```
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020182823114.png"/>
|
||
|
||
注意如果你返回值写index而不是index.jsp,它会给你报404。意思就是它不会给我们加jsp后缀。
|
||
|
||
|
||
|
||
### 返回到DispatcherServlet#doDispatch()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020183211173.png">
|
||
|
||
|
||
|
||
### DispatcherServlet#applyDefaultViewName()
|
||
|
||
```java
|
||
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
|
||
if (mv != null && !mv.hasView()) { //如果没有指定跳转的页面
|
||
String defaultViewName = getDefaultViewName(request); //给一个默认页面
|
||
if (defaultViewName != null) {
|
||
mv.setViewName(defaultViewName);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
protected String getDefaultViewName(HttpServletRequest request) throws Exception {
|
||
return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
|
||
}
|
||
|
||
@Nullable //把请求转成视图名(我们要跳转的页面地址)的翻译器【没啥用】
|
||
private RequestToViewNameTranslator viewNameTranslator;
|
||
```
|
||
|
||
这里不是重点,我直接告诉你结果吧,默认页面就是把request的请求路径直接拿来当要去的页面地址 。
|
||
|
||
比如你的请求路径是`@GetMapping("/hello.html")`,但是你返回值写的是void,那么它就会给你返回到hello.html页面
|
||
|
||
|
||
|
||
|
||
|
||
### 返回到DispatcherServlet#doDispatch()
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020183829404.png" />
|
||
|
||
来到拦截器的后置处理环节
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020183904018.png" />
|
||
|
||
然后来到处理结果环节
|
||
|
||
### springmvc能写哪些返回值
|
||
|
||
官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-return-types
|
||
|
||
## 视图解析器解析流程
|
||
|
||
### DispatcherServlet#processDispatchResult()处理返回值
|
||
|
||
```java
|
||
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
|
||
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
|
||
@Nullable Exception exception) throws Exception {
|
||
|
||
boolean errorView = false;
|
||
|
||
if (exception != null) { //如果有异常处理异常
|
||
if (exception instanceof ModelAndViewDefiningException) {
|
||
logger.debug("ModelAndViewDefiningException encountered", exception);
|
||
mv = ((ModelAndViewDefiningException) exception).getModelAndView();//即使有异常,这里也会返回ModelAndView
|
||
}
|
||
else { //定义无数种异常解析器就会得到不同的异常解析效果
|
||
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
|
||
mv = processHandlerException(request, response, handler, exception);
|
||
errorView = (mv != null);
|
||
}
|
||
}
|
||
|
||
// 动态策略。 Did the handler return a view to render? 为啥?@ResponseBody(提前在解析返回值的时候,就已经把数据写出去了,所以这一步就没有了)
|
||
if (mv != null && !mv.wasCleared()) {
|
||
render(mv, request, response); //渲染ModeAndView,来解析模型和视图;最终决定响应效果
|
||
if (errorView) {
|
||
WebUtils.clearErrorRequestAttributes(request);
|
||
}
|
||
}
|
||
else {
|
||
if (logger.isTraceEnabled()) {
|
||
logger.trace("No view rendering, null ModelAndView returned.");
|
||
}
|
||
}
|
||
|
||
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
|
||
// Concurrent handling started during a forward
|
||
return;
|
||
}
|
||
|
||
if (mappedHandler != null) {
|
||
// Exception (if any) is already handled..
|
||
mappedHandler.triggerAfterCompletion(request, response, null);
|
||
}
|
||
}
|
||
```
|
||
|
||
进入此方法
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020184308914.png"/>
|
||
|
||
|
||
|
||
### DispatcherServlet#render()渲染ModelAndView
|
||
|
||
```java
|
||
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||
// Determine locale for request and apply it to the response.
|
||
Locale locale =
|
||
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
|
||
response.setLocale(locale);
|
||
|
||
View view;
|
||
String viewName = mv.getViewName();
|
||
if (viewName != null) {
|
||
// We need to resolve the view name. 关键还是这里
|
||
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
|
||
if (view == null) {
|
||
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
|
||
"' in servlet with name '" + getServletName() + "'");
|
||
}
|
||
}
|
||
else {
|
||
// No need to lookup: the ModelAndView object contains the actual View object.
|
||
view = mv.getView();
|
||
if (view == null) {
|
||
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
|
||
"View object in servlet with name '" + getServletName() + "'");
|
||
}
|
||
}
|
||
|
||
// Delegate to the View object for rendering.
|
||
if (logger.isTraceEnabled()) {
|
||
logger.trace("Rendering view [" + view + "] ");
|
||
}
|
||
try {
|
||
if (mv.getStatus() != null) {
|
||
response.setStatus(mv.getStatus().value());
|
||
}
|
||
view.render(mv.getModelInternal(), request, response);
|
||
}
|
||
catch (Exception ex) {
|
||
if (logger.isDebugEnabled()) {
|
||
logger.debug("Error rendering view [" + view + "]", ex);
|
||
}
|
||
throw ex;
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
### DispatcherServlet#resolveViewName()
|
||
|
||
```java
|
||
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
|
||
Locale locale, HttpServletRequest request) throws Exception {
|
||
|
||
if (this.viewResolvers != null) {
|
||
//一样的for循环视图解析器
|
||
for (ViewResolver viewResolver : this.viewResolvers) {
|
||
View view = viewResolver.resolveViewName(viewName, locale);
|
||
if (view != null) {
|
||
return view;
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
```
|
||
|
||
不过默认的视图解析器只有一个
|
||
|
||
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_10/image-20211020184754506.png"/>
|
||
|
||
这里没有拼接前缀和后缀,所以我们必须要自己写.jsp或者.html
|
||
|
||
### RequestResponseBodyMethodProcessor即是返回值解析器也是参数解析器
|
||
|
||
```java
|
||
@Override
|
||
public boolean supportsParameter(MethodParameter parameter) {
|
||
return parameter.hasParameterAnnotation(RequestBody.class);
|
||
}
|
||
|
||
@Override
|
||
public boolean supportsReturnType(MethodParameter returnType) {
|
||
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
|
||
returnType.hasMethodAnnotation(ResponseBody.class));
|
||
}
|
||
|
||
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
|
||
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
|
||
|
||
parameter = parameter.nestedIfOptional();
|
||
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
|
||
String name = Conventions.getVariableNameForParameter(parameter);
|
||
|
||
if (binderFactory != null) {
|
||
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
|
||
if (arg != null) {
|
||
validateIfApplicable(binder, parameter);
|
||
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
|
||
throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
|
||
}
|
||
}
|
||
if (mavContainer != null) {
|
||
mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
|
||
}
|
||
}
|
||
|
||
return adaptArgumentIfNecessary(arg, parameter);
|
||
}
|
||
|
||
@Override //如果返回值标注了 @ResponseBody注解。会被这个人拦截处理器
|
||
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);
|
||
|
||
// Try even with null return value. ResponseBodyAdvice could get involved.
|
||
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
|
||
}
|
||
```
|
||
|
||
|
||
|
||
由于现在的前后端分离时代,我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据,所以视图解析器详细原理不再细述,后面就直接开始讲异常处理流程。
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|