Files
JavaYouth/docs/spring-sourcecode-v1/09.第9章-SpringMVC请求处理源码和HandlerMapping原理.md
2022-07-24 20:30:09 +08:00

1246 lines
37 KiB
Markdown
Raw Permalink 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.
---
title: Spring源码系列-第9章-SpringMVC请求处理源码和HandlerMapping原理
tags:
- Spring源码
categories:
- Spring
- 源码V1
keywords: Spring框架spring源码
description: SpringMVC请求处理源码和HandlerMapping原理
cover: 'https://npm.elemecdn.com/lql_static@latest/logo/spring.png'
abbrlink: 6f2cef28
date: 2022-06-21 12:01:02
---
# 第9章-SpringMVC请求处理源码和HandlerMapping原理
## 请求的处理链路
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211017194744212.png"/>
1. tomcat里面可以部署多个项目应用。/abc_test和mvc_test这种就是项目路径用于区分多个项目
2. 在以前的Servlet开发中每一个路径都需要有一个Servlet来处理。比如上图所画
3. 有了SpringMVC整个Tomcat下面就不会有很多Servlet了只会有一个DispatcherServlet来统一处理
### Servlet继承树
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211017195927505.png"/>
1. Servlet里是Service()方法处理请求
2. GenericServlet也是Service()方法处理请求
3. HttpServlet开始根据请求类型将处理方法分为doGetdoPostdoPutdoDelete等等
4. HttpServletBean没有处理方法
5. FrameworkServlet也是有处理方法分别为doGetdoPostdoPutdoDelete等等但是这些方法最终调用的都是`processRequest(HttpServletRequest request, HttpServletResponse response)`
6. DispatcherServlet处理方法是doService()
### Debug调用栈
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211017205544154.png" />
### DispatcherServlet#doService()
```java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null; //把request域的所有属性提前保存
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName)); //快照所有属性
}
}
}
//基本的东西保存到request域中方便处理 Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); //国际化解析器
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); //主题解析器
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) { //闪存管理器(重定向携带数据)
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
RequestPath previousRequestPath = null;
if (this.parseRequestPath) {
previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
ServletRequestPathUtils.parseAndCache(request);
}
try {
doDispatch(request, response); //处理派发功能
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
}
}
```
### DispatcherServlet#doDispatch()请求派发
```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;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 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);
}
}
}
}
```
### doDispatch处理大流程图
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/doDispatch处理大流程.jpg">
### DispatcherServlet#checkMultipart()
```java
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
//使用文件上传解析器来判断是否文件上传请求
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
// ......
}
// If not returned before: return original request.
return request;
}
```
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211017205843256.png" />
咱们这里目前连解析器都没有,所以就直接返回了
### StandardServletMultipartResolver#isMultipart()
```java
public boolean isMultipart(HttpServletRequest request) {
//所有文件上传请求头Content-Type都会有这个
return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
}
```
### DispatcherServlet九大组件
```java
//DispatcherServlet中的九大组件、全是接口我们完全可以自定义实现。Spring默认也都准备好了这些组件的实现
/** MultipartResolver used by this servlet. */
@Nullable //文件上传解析器
private MultipartResolver multipartResolver;
/** LocaleResolver used by this servlet. */
@Nullable //国际化解析器 Locale区域信息
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet. */
@Nullable //主题解析器
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet. */
@Nullable //Handler处理器、能处理请求的人Controller的映射【保存的就是所有请求都由谁来处理的映射关系】
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet. */
@Nullable //Handler处理器的适配器是一个超级反射工具帮我们解决参数呀返回值这些不需要我们很麻烦的写
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet. */
@Nullable //Handler的异常解析器异常处理功能
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet. */
@Nullable //把请求转成视图名(我们要跳转的页面地址)的翻译器【没啥用】
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet. */
@Nullable //闪存管理器
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet. */
@Nullable //视图解析器(我们去哪些页面,怎么过去?)
private List<ViewResolver> viewResolvers;
```
[官网介绍](https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-servlet-special-bean-types)
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211017210137817.png" />
## 九大组件利用Spring事件机制进行初始化
上面的图我们看到九大组件有八个已经有值了我们现在看下他们是何时有值的。怎么找就很简单你就找这些属性的setXXX方法最后我找到了这里
### DispatcherServlet#initStrategies()
```java
protected void initStrategies(ApplicationContext context) { //初始化所有策略,九大组件在这里进行了初始化
initMultipartResolver(context); //容器中有就用没有就是null
initLocaleResolver(context); //从容器中获取,没有用默认
initThemeResolver(context); //从容器中获取,没有用默认
initHandlerMappings(context); //从容器中获取,没有用默认
initHandlerAdapters(context); //从容器中获取,没有用默认
initHandlerExceptionResolvers(context); //从容器中获取,没有用默认
initRequestToViewNameTranslator(context); //Spring没有浓重说他//从容器中获取,没有用默认
initViewResolvers(context); //从容器中获取,没有用默认
initFlashMapManager(context); //从容器中获取,没有用默认
}
```
代码几乎都是一样的逻辑,咱们就只看一个举个例子
### DispatcherServlet#initLocaleResolver()
```java
private void initLocaleResolver(ApplicationContext context) {
try { //容器中先来获取
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default. 容器中没有,读取默认配置文件进行加载
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); //获取默认策略
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
```
只要不满意九大组件的任何一个自己可以实现指定的接口这样就会放在容器中SpringMVC
### Debug调用栈
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018162558076.png" />
1. Tomcat启动
2. 触发DispatcherServlet的初始化
3. DispatcherServlet初始化全部结束容器会发送Spring的相关事件.
4. 感知到容器准备好了的事件--初始化九大组件(底层是SourceFilteringListener, 把事件回调到DispatcherServlet的onRefresh方法)
### DispatcherServlet#onRefresh()接收到事件开始准备初始化九大组件
```java
protected void onRefresh(ApplicationContext context) {
initStrategies(context); //初始化九大组件
}
```
### DispatcherServlet#getDefaultStrategy() 获取默认初始化策略
```java
protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
List<T> strategies = getDefaultStrategies(context, strategyInterface);
if (strategies.size() != 1) {
throw new BeanInitializationException(
"DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
}
return strategies.get(0);
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
if (defaultStrategies == null) {
try {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers. 去DispatcherServlet所在的类路径下找一个 DispatcherServlet.properties 资源
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); //读取properties文件
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
// ......
}
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
```
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018164740715.png" />
#### DispatcherServlet.properties
下面这些就是九大组件默认组件
```properties
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
```
## HandlerMapping处理URL和Controller的映射关系
### DispatcherServlet#doDispatch()请求派发
```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;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
// ......
}
```
### DispatcherServlet#getHandler()根据请求拿Controller
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018165759982.png" />
这里会有三个默认的HandlerMapping就是在DispatcherServlet.properties写的那三个在之前说九大组件初始化的时候如果我们自己没有写就获取默认的
1. BeanNameUrlHandlerMappingbean的名字作为URL路径进行映射
2. RequestMappingHandlerMapping@RequestMapping注解作为URL地址进行映射
1. 默认用它,而且它里面保存了所有请求映射信息
3. RouterFunctionMapping: 支持函数式处理以及WebFlux相关功能未来可能这个用的多
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018170430551.png"/>
BeanNameUrlHandlerMapping里找不到映射关系就直接下一个循环了。咱们主要看RequestMappingHandlerMapping怎么处理的
## RequestMappingHandlerMapping处理流程
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018170624402.png" />
RequestMappingHandlerMapping的父类有一个MappingRegistry属性保存了请求路径 ==> 请求Controller+方法的映射
F7进入`mapping.getHandler(request)`
### AbstractHandlerMapping#getHandler()开始根据请求URL查哪个Controler能处理
```java
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request); //HandlerMapping的registry中找映射返回HandlerMethod真正执行当前请求的方法
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
//找到前面的目标方法以后,还要构造一个处理器链;
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
```
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018172011789.png" />
F7进入`getHandlerInternal(request)`
### RequestMappingInfoHandlerMapping#getHandlerInternal()
```java
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
try {
return super.getHandlerInternal(request);
}
finally {
ProducesRequestCondition.clearMediaTypesAttribute(request);
}
}
```
继续F7进`super.getHandlerInternal(request)`
### AbstractHandlerMethodMapping#getHandlerInternal()
```java
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); //寻找当前请求谁能处理
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
```
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018172342398.png" />
F7进入`lookupHandlerMethod(lookupPath, request)`
### AbstractHandlerMethodMapping#lookupHandlerMethod()真正根据URL查Controller
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018173123281.png"/>
可以看到还是从mappingRegistry里获取。我们后面再看mappingRegistry是什么时候被放入东西的这里我们接着返回
### 返回到AbstractHandlerMethodMapping#getHandlerInternal()
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018185954647.png"/>
这个时候是已经找到了由哪个处理器处理,接着返回
### 返回到AbstractHandlerMapping#getHandler()
返回到这一步,准备执行`getHandlerExecutionChain(handler, request)`
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018190338925.png" />
### AbstractHandlerMapping#getHandlerExecutionChain()开始构造处理器链
```java
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//把系统中所有的拦截器拿过来
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor); //所有拦截器加进去
}
}
return chain;
}
```
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018190711035.png" />
- 咱们没写拦截器,就没有。
- 继续往回返
### 返回到DispatcherServlet#getHandler()
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018190924151.png" />
这里就是责任链模式有能处理的handler就直接返回
### 返回到DispatcherServlet#doDispatch()
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018191048955.png" />
自此RequestMappingHandlerMapping处理结束
## RequestMappingHandlerMapping如何保存映射关系
上面我们看到了实际上是从MappingRegistry里拿到URL-->XXXController的映射关系的那么下面就来演讲MappingRegistry是何时保存的这映射关系又是怎样保存的
### AbstractHandlerMethodMapping#registerMapping()
我们这里还是靠猜测,猜它调用哪个方法。找到了如下方法,给第一行打断点,重启。
```java
//分析所有的Controller里面的每一个@RequestMapping 注解才能知道这个事情
public void registerMapping(T mapping, Object handler, Method method) { //哪个请求由哪个方法处理会通过这个进行注册
if (logger.isTraceEnabled()) {
logger.trace("Register \"" + mapping + "\" to " + method.toGenericString());
}
this.mappingRegistry.register(mapping, handler, method);
}
```
重启的时候没有发现没跑到这里然后我就看了下这个MappingRegistry发现它是个AbstractHandlerMethodMapping的内部类
#### MappingRegistry(URL=>XXXController的映射中心)
```java
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
// 在这里put的
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
}
```
我发现上面有一个registry是个Map类型很容易想到这应该就是保存映射的然后我就在它的put方法那里打断点只找到了这一个put。重启应用
### Debug调用栈
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018174537002.png" />
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018174827232.png" />
意料之中启动应用的时候从init初始化那里调用过来了
#### 流程概述
1. `DispatcherServlet#onRefresh()`开始初始化九大组件就会开始初始化HandlerMapping
2. 首先是创建`DispatcherServlet.properties`里指定的三个HandlerMapping实现类的对象。还是用createBean来创建HandlerMapping的
3. 其中RequestMappingHandlerMapping创建完对象后因为它实现了InitializingBean所以会调用RequestMappingHandlerMapping#afterPropertiesSet()
4. 接着就是拿到Web子容器的所有组件for循环处理。看是不是有@Controller注解或者@RequestMapping注解
5. 最后把分析到的RequestMapping信息放到HandlerMapping的registry中
### RequestMappingHandlerMapping#afterPropertiesSet()
```java
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
if (getPatternParser() != null) {
this.config.setPatternParser(getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
"Suffix pattern matching not supported with PathPatternParser.");
}
else {
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(getPathMatcher());
}
super.afterPropertiesSet();
}
```
### AbstractHandlerMethodMapping#afterPropertiesSet()
```java
public void afterPropertiesSet() {
initHandlerMethods(); //初始化HandlerMethods。
}
```
### AbstractHandlerMethodMapping#initHandlerMethods()初始化HandlerMethods
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018175412239.png" />
可以看到这里只拿了Web子容器的所有组件进行for循环
### AbstractHandlerMethodMapping#processCandidateBean()
```java
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName); //分析当前bean的HandlerMethods
}
}
```
### RequestMappingHandlerMapping#isHandler()判断当前bean是不是Handler
```java
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
```
1. 这里首先会过滤Bean只有你Bean也就是类上标注了@Controller注解或者@RequestMapping注解,才会给这个类走下面的流程
2. 那这个是什么意思呢?
3. 平常我们写XXXController就是写一个@Controller注解就完事了【注意@RestController值是@Controller的复合注解,包含了@Controller
4. 实际上我们可以不写@Controller注解,直接@Component+@RequestMapping也是一样的效果
### AbstractHandlerMethodMapping#detectHandlerMethods()探查容器里满足条件的方法
1. 然后那个detectHandlerMethods就是探查容器里满足条件的方法
2. 下面看下怎么探索的
```java
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType); //探索当前类里面所有满足的方法,所有标注了 RequestMapping
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
```
<img src="https://npm.elemecdn.com/youthlql@1.0.6/spring-sourcecode-v1/chapter_09/image-20211018183141571.png" />
### RequestMappingHandlerMapping#getMappingForMethod()
```java
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = createRequestMappingInfo(method); //为每一个方法尝试创建 RequestMappingInfo
if (info != null) {
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
info = typeInfo.combine(info);
}
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
```
### RequestMappingHandlerMapping#createRequestMappingInfo(AnnotatedElement )
```java
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 找每一个标有@RequestMapping注解的方法
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
```
自此一切就明了了原理就是上面写的流程概述。for循环组件@RequestMapping注解
### 小总结
Q功能增强的时候什么时候用后置处理器BeanPostProcessor什么时候用生命周期InitializingBean呢
A
1. 如果是所有组件都可能会用到的增强功能那就实现后置处理器BeanPostProcessor来增强
2. 如果是单组件增强最好用InitializingBean。可以看到mvc的RequestMappingHandlerMapping这里用的就是InitializingBean