介紹
一個@RequestMapping請求的流程如下
- 通過RequestMappingHandlerMapping傳回HandlerMethod這種類型的handler
- 找到能處理HandlerMethod的RequestMappingHandlerAdapter
- RequestMappingHandlerAdapter執行業務邏輯傳回ModelAndView
Spring容器啟動的時候,會将@RequestMapping中的資訊和要執行的方法存放在AbstractHandlerMethodMapping的内部類MappingRegistry中的5個成員變量中,其中的T為RequestMappingInfo,是對@RequestMapping注解資訊的封裝
class MappingRegistry {
// MappingRegistration包含
// 1. 映射資訊
// 2. 映射資訊中的直接路徑(非模式化路徑)
// 3. 映射名
// 4. 對應的處理器方法
// 分别為下面4個map的key
// 這個隻是對4個key的封裝,隻在unregister方法中會使用
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
// RequestMappingInfo -> HandlerMethod
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
// 不包含通配符的url -> List<RequestMappingInfo>
// 這裡為什麼是一個List呢?因為一個url有可能對應多個方法,即這些方法的@RequestMapping注解path屬性一樣,但是其他屬性不一樣
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
// 映射名 -> HandlerMethod
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
// HandlerMethod -> 跨域配置
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
}
中最主要的是如下2個map,其餘的本節關聯不大,不再分析
urlLookup:儲存了如下映射關系
key=不包含通配符的url
value=List<RequestMappingInfo>
mappingLookup:
key=RequestMappingInfo
value=HandlerMethod
為什麼需要這2個map呢?先留着這個問題,我們繼續往後看
RequestMappingHandlerMapping的查找過程
RequestMappingHandlerMapping的繼承關系如下
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQDOxEzX3xCZlhXam9VbsUmepNXZy9CXwJWZ3xCdh1mcvZ2Lc1zaHRGcWdUYuVzVa9GczoVdG1mWfVGc5RHLwIzX39GZhh2csATMflHLwEzX4xSZz91ZsAzMfRHLGZkRGZkRfJ3bs92YskmNhVTYykVNQJVMRhXVEF1X0hXZ0xiNx8VZ6l2cssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL5QTM3cTZ5QTO2EzN5MGMyYzX4UzNxQTM2EzLcJDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
HandlerMapping接口一個getHandler方法,用來根據請求的url找到對應的HandlerExecutionChain對象。
public interface HandlerMapping {
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
而HandlerExecutionChain對象就是簡單的将url對應的Handler和攔截器封裝了一下
public class HandlerExecutionChain {
private final Object handler;
@Nullable
private List<HandlerInterceptor> interceptorList;
}
通過debug發現,整個查找過程隻是依次調用了如下3個方法
- AbstractHandlerMapping#getHandler
- AbstractHandlerMethodMapping#getHandlerInternal
- AbstractHandlerMethodMapping#lookupHandlerMethod
AbstractHandlerMapping#getHandler
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// getHandlerInternal 是模闆方法,留給子類去實作
Object handler = getHandlerInternal(request);
if (handler == null) {
// 沒有擷取到使用預設的handler
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
// 如果handler是string類型,則以它為名從容器中查找相應的bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 拿到handler,和我們配置的攔截器,封裝成HandlerExecutionChain對象傳回
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
// 跨域配置
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
可以看到找handler的過程是交由子類去實作的。并将對這個請求适用的攔截器,跨域配置,handler封裝成HandlerExecutionChain傳回給DispatcherServlet
AbstractHandlerMethodMapping#getHandlerInternal
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 擷取請求路徑,作為查找路徑
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
if (logger.isDebugEnabled()) {
logger.debug("Looking up handler method for path " + lookupPath);
}
// 擷取讀鎖
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
if (logger.isDebugEnabled()) {
if (handlerMethod != null) {
logger.debug("Returning handler method [" + handlerMethod + "]");
}
else {
logger.debug("Did not find handler method for [" + lookupPath + "]");
}
}
// handlerMethod就是要執行的controller方法的封裝
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
這一步做的事情也不是很多,就是根據url找到對應的HandlerMethod
AbstractHandlerMethodMapping#lookupHandlerMethod
兜兜轉轉終于來到根據請求擷取相應的HandlerMethod對象了
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// 比對結果清單
List<Match> matches = new ArrayList<>();
// 直接比對
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
// 如果有比對的,就添加進比對清單中
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
// 還沒有比對,就周遊所有的處理方法去找
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
// 比對結果不為空
if (!matches.isEmpty()) {
// 比對結果比較器,直接使用比對資訊進行比較
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
// 對多個比對結果進行排序
matches.sort(comparator);
if (logger.isTraceEnabled()) {
logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
}
// 最佳比對
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
// 第一個元素和第二個元素進行比較
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
// 結果為0,至少有2個最佳比對
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
// 多個最佳比對,抛出異常
throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
// 傳回最佳比對對應的處理器方法
return bestMatch.handlerMethod;
}
else {
// 沒有比對結果,執行無處理器比對方法,可執行一些特殊邏輯
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
這個方法就涉及到我們最開始提到的那2個map。
urlLookup
key=不包含通配符的url
value=List<RequestMappingInfo>
mappingLookup
key=RequestMappingInfo
value=HandlerMethod
- 先根據url從urlLookup中查找對應的RequestMappingInfo,如果找到的List<RequestMappingInfo>不為空,則判斷其他比對條件是否符合
- 如果其他條件也有符合的,則不再周遊所有的RequestMappingInfo,否則周遊所有的RequestMappingInfo,因為考慮到有通配符形式的url必須周遊所有的RequestMappingInfo才能找出來符合條件的
- 如果最終找到的RequestMappingInfo有多個,則按照特定的規則找出一個最比對的,并傳回其對應的HandlerMethod
可能有小夥伴有很多疑惑,為什麼一個請求請求位址會有多個實作。我給你舉一個例子,有3個相同的handler方法,@RequestMapping中的其他屬性都相同,隻是method不同,這樣就有可能根據一個url找到3個RequestMappingInfo,這樣就有選擇一個最優RequestMappingInfo的過程
當然還有其他情況,比如一個url同時比對2個通配符路徑。
可以看出來查找的過程還是挺簡單的,接着我們就來分析一下注冊的過程
RequestMappingHandlerMapping的初始化過程
從上面的分析中我們可以得出結論,在Spring容器啟動後,RequestMappingInfo和HandlerMethod的映射關系已經被儲存到MappingRegistry對象中,那麼它是多會儲存的以及以何種形式儲存的?
通過檢視調用關系法相,MappingRegistry中各種映射關系的初始化在register()方法,是以我們隻需要在其register()方法上加一個斷點,檢視其調用鍊路即可
- RequestMappingHandlerMapping#afterPropertiesSet
- AbstractHandlerMethodMapping#afterPropertiesSet
- AbstractHandlerMethodMapping#initHandlerMethods
- AbstractHandlerMethodMapping#detectHandlerMethods
- AbstractHandlerMethodMapping#registerHandlerMethod
從RequestMappingInfoHandlerMapping可以看出AbstractHandlerMethodMapping中的泛型類為RequestMappingInfo
RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo>
接着仔細分析這些方法
RequestMappingHandlerMapping#afterPropertiesSet
@Override
public void afterPropertiesSet() {
// 建立映射資訊構造器配置,用于構造映射資訊RequestMappingInfo
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
// 設定内容協商管理器元件
this.config.setContentNegotiationManager(getContentNegotiationManager());
// 執行處理器方法的初始化邏輯
super.afterPropertiesSet();
}
主要做了一些初始化設定
AbstractHandlerMethodMapping#afterPropertiesSet
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
單純調用initHandlerMethods方法
AbstractHandlerMethodMapping#initHandlerMethods
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
// 擷取ApplicationContext中所有BeanName
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
// 排除Scoped目标類型Bean
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
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.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
// 如果擷取的BeanName類型不為空,且是一個處理器類型的Bean
// 如果beanType上有Controller注解或者RequestMapping注解則是handler
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
從容器中拿到所有的beanName,排除Scope類型的bean,根據beanName得到beanType,如果一個beanType是一個handler,則調用AbstractHandlerMethodMapping#detectHandlerMethods方法
AbstractHandlerMethodMapping#detectHandlerMethods
注冊的重頭戲就在這裡了
protected void detectHandlerMethods(Object handler) {
// 如果傳入的handler是String類型,則表示是一個BeanName,從上下文擷取
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
// Method -> Method上對應的RequestMappingInfo
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
methods.forEach((method, mapping) -> {
// 擷取真實的可執行的方法,因為上述查找邏輯在特殊情況下查找到的方法可能存在于代理上
// 需要擷取非代理方法作為可執行方法調用
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
就是擷取Handler上的Method,以及根據Method上@RequestMapping注解封裝的RequestMappingInfo,
最後根據Handler,Method,RequestMappingInfo将映射關系注冊到map中
AbstractHandlerMethodMapping#registerHandlerMethod
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
AbstractHandlerMethodMapping#MappingRegistry#register
public void register(T mapping, Object handler, Method method) {
// 擷取寫鎖
this.readWriteLock.writeLock().lock();
try {
// 把類和方法封裝為HandlerMethod
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 保證方法映射唯一
// 如果一個相同的url對應多個handlerMethod則會抛出異常
assertUniqueMethodMapping(handlerMethod, mapping);
// 向映射查找表中添加 映射資訊->對應的處理器方法
this.mappingLookup.put(mapping, handlerMethod);
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
// 存儲 不帶統配符的url -> RequestMappingInfo 的映射關系
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
String name = null;
// 命名政策不為空
if (getNamingStrategy() != null) {
// 通過命名政策擷取映射名
name = getNamingStrategy().getName(handlerMethod, mapping);
// 注冊到nameLookup
addMappingName(name, handlerMethod);
}
// 擷取HandlerMethod對應的CORS配置
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 注冊跨域配置
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}