Files
JavaYouth/docs/spring-sourcecode-v1/10.第10章-SpringMVC中的HandlerAdapter源码解析.md

1359 lines
51 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第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 {
//把原生的requestresponse封装到一个对象中方便后续只用这一个参数就行【装饰器模式】
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里的属性就是agenamesex这些的
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);
}
```
由于现在的前后端分离时代,我们直接返回视图的需求已经很少很少了。现在一般都是直接返回数据,所以视图解析器详细原理不再细述,后面就直接开始讲异常处理流程。