1. HandlerMapping 概述
HandlerMapping 的存在其實(shí)就是解決 HttpServletRequest 與 Handler 之間映射關(guān)系!對應(yīng)的子類主要分為基于 BeanName, ClassName, 注解信息來獲取對應(yīng)的映射的請求! 其子類如下:

1. HandlerMapping: 這個(gè)接口中定義了通過HttpServletRequest 來獲取對應(yīng)的 HandlerExecutionChain(PS: HandlerExecutionChain 中定義了請求的攔截器+最終調(diào)用的 Handler), 其中還有一個(gè)非常重要的變量 URI_TEMPLATE_VARIABLES_ATTRIBUTE, 這個(gè)key對應(yīng)的value其實(shí)就是 uri template 變量(@PathVariable 解析屬性時(shí)獲取數(shù)據(jù)的來源)
2. AbstractHandlerMapping: 這個(gè)類繼承了 WebApplicationObjectSupport, 這就讓其具有通過 ApplicationContextAware 接口來觸發(fā)初始化的功能, 當(dāng)然其也在初始化方法中獲取了 MappedInterceptor, 并且定義了通過 HttpServletRequest 獲取 HandlerExecutionChain 的主邏輯 <-- 其也留下了獲取 Handler 的模版方法 getHandlerInternal
3. AbstractHandlerMethodMapping: 通過 HttpServletRequest 來獲取 HandlerMethod; 其通過 InitializingBean.afterPropertiesSet 來搜索獲取所有 HandlerMethod <-- 這部分就是在HandlerMapping初始化時(shí)獲取所有HandlerMethod; 并且留下了 getMappingForMethod 等獲取 RequestMappingInfo 的模版方法
4. AbstractUrlHandlerMapping: 與 AbstractHandlerMethodMapping 不同, AbstractUrlHandlerMapping.getHandlerInternal 返回的是個(gè) Object(PS: 這里的Object有可能是 ApplicationContext 中的 BeanName, 或直接是個(gè) Bean), 而且其中完成了 URI 與 handler 的注冊流程 registerHandler(urlPath, beanName)
5. RequestMappingHandlerMapping: 基于 HandlerMethod 的HandlerMapping, 主要實(shí)現(xiàn)了getMappingForMethod(基于Method,handlerType獲取RequestMappingInfo), 而 RequestMappingInfoHandlerMapping 中主要是在查找 HandlerMethod 時(shí)對 HttpServletRequest 中的一些操作, 比如設(shè)置 uri template variable
6. SimpleUrlHandlerMapping: 在配置 uri 與handler 之間映射關(guān)系的 HandlerMapping (PS: 這里的 handler 可以是任意類型, 方正可以有對應(yīng)的 HandlerAdapter 來進(jìn)行激活它)
7. AbstractDetectingUrlHandlerMapping: 從類名中我們就可以獲知, 這是一個(gè)自動(dòng)獲取 Handler 已經(jīng) url 的類, 這個(gè)方法的觸發(fā)操作是 ApplicationContextAware.setApplicationContext
8. BeanNameUrlHandlerMapping: 以 BeanName 為 uri 的 HandlerMapping <-- 其中 BeanName 必需以 "/" 開頭
9. ControllerBeanNameHandlerMapping: 通過 BeanName 構(gòu)成 uri 的 HandlerMapping (PS: 這個(gè)類已經(jīng)過期)
10. ControllerClassNameHandlerMapping: 基于 className 生成 uri 的 HandlerMapping(PS: 這個(gè)類已經(jīng)過期)
11. DefaultAnnotationHandlerMapping: 基于 @RequestMapping 的 HandlerMapping, 這里面會出現(xiàn) 多個(gè) urls 對應(yīng)一個(gè) Handler (PS: 這個(gè)類已經(jīng)過期)
2. HandlerMapping 抽象類 AbstractHandlerMapping
AbstractHandlerMapping 中定義了獲取 Handler 的主邏輯; 其主要有一下屬性
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
// 默認(rèn)請求處理器, 在找不到 Handler時(shí)返回的默認(rèn)值
private Object defaultHandler;
// 獲取 url 的工具類
private UrlPathHelper urlPathHelper = new UrlPathHelper();
// 路徑匹配器 <- 進(jìn)行正則之類匹配的操作
private PathMatcher pathMatcher = new AntPathMatcher();
// Handler 處理攔截器 <- 最后還是會將攔截器放入 adaptedInterceptors 中
private final List<Object> interceptors = new ArrayList<Object>();
// Handler 處理攔截器, 這個(gè)與 interceptors不同, 在封裝 Chain 時(shí)其實(shí)使用的是 adaptedInterceptors
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
// 跨域資源共享(Cross-origin resource sharing)
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
從上面屬性我們發(fā)現(xiàn)了 HandlerInterceptor, 這是一個(gè)作用于 handler 之前與之后的攔截器, 對應(yīng)的獲取操作是在 ApplicationContextAware.setApplicationContext 中, 方法也很簡單, 直接通過ApplicationContext獲取其中的所有 HandlerInterceptor 類型的 Bean
// 通過 initApplicationContext 方法進(jìn)行初始化, 其一般是由父類執(zhí)行 ApplicationContextAware#setApplicationContext() 方法間接調(diào)用
// 主要的目的是獲取 springMVC 上下文中的攔截器集合, 特指 MappedInterceptor
@Override
protected void initApplicationContext() throws BeansException {
// 供子類擴(kuò)展添加攔截器, 目前 spring 沒有自行實(shí)現(xiàn)
extendInterceptors(this.interceptors);
// 搜索 springMVC 中的 MappedInterceptors 保存至 adaptorInterceptors 集合
detectMappedInterceptors(this.adaptedInterceptors);
// 將 interceptors 集合添加至 adaptedInterceptors 集合
initInterceptors();
}
而獲取 HandlerExecutionChain 的主邏輯也在這里進(jìn)行類實(shí)現(xiàn), 當(dāng)然留下了另外一個(gè)模版方法 getHandlerInternal(request)
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 獲取到的 handler 對象一般為 bean/HandlerMathod, 它是方法為抽象方法, 供子類實(shí)現(xiàn)
Object handler = getHandlerInternal(request); // 正真調(diào)用的是 AbstractHandlerMethodMapping#getHandlerInternal()
// 上述找不到則使用默認(rèn)的處理類, 沒有設(shè)定則返回 null, 則會返回前臺 404 錯(cuò)誤
if (handler == null) handler = getDefaultHandler();
// 如果也沒有提供默認(rèn)的 handler, 則無法繼續(xù)處理返回 null
if (handler == null) return null;
// 若 handler 是 beanName, 則 通過名稱取出對應(yīng)的 Handler bean
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// 創(chuàng)建處理鏈對象 <-- 其中就是封裝 HandlerInterceptor 與 Handler
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
return executionChain;
}
3. AbstractHandlerMapping 的子類 AbstractHandlerMethodMapping
相對于AbstractHandlerMapping, AbstractHandlerMethodMapping 主要完成了Handler的獲取操作, 對應(yīng)代碼在getHandlerInternal中, 通過 uri 與 HttpServletRequest 獲取 HandlerMethod
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 獲取訪問的路徑, 一般類似于 request.getServletPath() 返回不含 contextPath 的訪問路徑
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 獲取讀鎖
this.mappingRegistry.acquireReadLock(); // 獲取讀寫所的讀鎖
try {
// 獲取 HandlerMethod 作為 handler 對象, 這里涉及到路徑匹配的優(yōu)先級
// 優(yōu)先級: 精確匹配 > 最長路徑匹配 > 擴(kuò)展名匹配
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
// handlerMethod 內(nèi)部含有 bean 對象, 其實(shí)指的是對應(yīng)的 Controller
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
// 釋放 度鎖
this.mappingRegistry.releaseReadLock();
}
}
上面是對應(yīng)的獲取邏輯, 與之對應(yīng)的還有HandlerMethod 的注冊邏輯, 注冊的入口在 InitializingBean.afterPropertiesSet, 大體流程如下:
1. 獲取 Spring 中所有注冊的 Bean 的Name, 并且一個(gè)一個(gè)獲取對應(yīng) BeanType
2. 判斷BeanType 是否被注解 @Controller 或 @RequestMapping 注解, 若是的話, 則進(jìn)行解析這個(gè) BeanType
3. 篩選 BeanType 中被 @RequestMapping 的方法
對應(yīng)的代碼如下:
protected void initHandlerMethods() {
// 獲取 springMVC 上下文的所有注冊的 Bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) { // 循環(huán)遍歷 ApplicationContext 中的 所有 beanName
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = getApplicationContext().getType(beanName);
}
catch (Throwable ex) {}
if (beanType != null && isHandler(beanType)) { // isHandler() 是抽象方法, 主要供子類 需要掃描什么類型的 bean <-- 判斷是否是 Handler
// 解析其中的 handlerMethod 進(jìn)行注冊 <-- 其中注冊中會注冊到好幾個(gè) Map 中 RequestMappingInfo <-> HandlerMethod, URI <-> HandlerMethod, name <-> HandlerMethod, RequestMappingInfo <-> MappingRegistration
detectHandlerMethods(beanName);
}
}
}
}
protected void detectHandlerMethods(final Object handler) {
// 獲取 handler 的類型 <- 若 handler 是 String, 則通過 ApplicationContext 直接獲取 真實(shí)的 Bean
Class<?> handlerType = (handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());
// 因?yàn)橛行┦?CGLIB 代理生成的, 獲取真實(shí)類
final Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
try {
return getMappingForMethod(method, userType); // 模板方法獲取 handlerMethod 的 mapping 屬性
}
catch (Throwable ex) {}
}
});
for (Map.Entry<Method, T> entry : methods.entrySet()) { // 對查找到的 HandlerMethod 進(jìn)行注冊, 默認(rèn)就是 被@RequestMapping 注解修飾的,保存到內(nèi)部類mappingRegistry 對象中
// 做下判斷, method 是否從屬于 userType
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue(); // 獲取 RequestMappingInfo 的信息
registerHandlerMethod(handler, invocableMethod, mapping); // 將 RequestMappingInfo <-> HandlerMethod, URI <-> HandlerMethod, name <-> HandlerMethod, RequestMappingInfo <-> MappingRegistration
}
}
其上只是獲取 Method 的操作, 而對應(yīng)正真注冊的工作交給 MappingRegistry 來完成, 而 MappingRegistry 中有如下屬性:
1. registry: RequestMappingInfo <--> MappingRegistration 的映射關(guān)系(MappingRegistration 是包含 HandlerMethod 與 RequestMappingInfo 的包裝類)
2. mappingLookup: RequestMappingInfo <--> HandlerMethod 的映射關(guān)系
3. urlLookup: uri <--> HandlerMethod 的映射關(guān)系
4. nameLookup: name <--> HandlerMethod 的映射關(guān)系
5. readWriteLock: 讀寫鎖, 在多線程環(huán)境中對共享資源(映射關(guān)系), 讀寫權(quán)限的控制
對應(yīng)的注冊邏輯如下:
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock(); // 獲取寫鎖
try { // 封裝 HandlerMethod <-- 其中有 Method, Bean, 以及對應(yīng)的 MethodParameter
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
assertUniqueMethodMapping(handlerMethod, mapping); // 斷言 mapping 沒有注冊過
if (logger.isInfoEnabled()) {
logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
}
this.mappingLookup.put(mapping, handlerMethod); // 將 RequestMethodInfo 與 HandlerMethod 放入 mappingLookup 中
List<String> directUrls = getDirectUrls(mapping); // 獲取 @RequestMapping 中 value | path 中的信息 <-- 并且不能含有 *|? (PS: 一個(gè) RequestMapping 具有多個(gè) path|value)
for (String url : directUrls) {
this.urlLookup.add(url, mapping); // 將 @RequestMapping 中的 value|path 注冊到 urlLookup 中
}
String name = null; // 獲取 @RequestMapping 中的 name 值
if (getNamingStrategy() != null) { // nameStrategy 其實(shí)就是 RequestMappingInfoHandlerMethodMappingNamingStrategy
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod); // 將 name <--> handlerMethod 加入 nameLookup
}
// Cors 跨域訪問的處理
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 將 RequestMappingInfo 與 MappingRegistration 注冊到 registry 中
this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
4. AbstractHandlerMethodMapping 的子類 RequestMappingHandlerMapping
RequestMappingHandlerMapping 是一個(gè)收集被@RequestMapping修飾的方法的 HandlerMapping, 相對其父類其主要完成了一下功能:
1. 根據(jù) Method, handlerType, 收集@RequestMapping, 并以此創(chuàng)建創(chuàng)建 RequestMappingInfo, 其中涉及到 RequestMappingInfo中的建造者模式(建造RequestMappingInfo), 策略模式(針對 @RequestMapping 中數(shù)據(jù)的不同條件判斷器 RequestCondition)
2. 根據(jù) beanType 上是否被 @Controller | @RequestMapping注解修飾來判斷 beanType 是否是符合的 Handler
主要代碼如下:
protected boolean isHandler(Class<?> beanType) { // class 是否是 Handler
// 通過 @Controller @RequestMapping 來進(jìn)行匹配
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
在這里還提到了將 @RequestMapping 解析后得到的對象 RequestMappingInfo, 這個(gè)類中包含了 @RequestMapping 注解中各個(gè)屬性的條件匹配器(匹配器主要有 combine: 組合兩個(gè)匹配器, getMatchingCondition: 得到兩個(gè)RequestCondition都匹配的 RequestCondition), 下面是其主要屬性:
// @RequestMapping 中的信息
private final String name;
// @RequestMapping 中 value|path 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并進(jìn)行匹配
private final PatternsRequestCondition patternsCondition;
// @RequestMapping 中 RequestMethod 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并進(jìn)行匹配
private final RequestMethodsRequestCondition methodsCondition;
// @RequestMapping 中 param 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并進(jìn)行匹配
private final ParamsRequestCondition paramsCondition;
// @RequestMapping 中 headers 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并進(jìn)行匹配
private final HeadersRequestCondition headersCondition;
// @RequestMapping 中 consumes 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并進(jìn)行匹配
private final ConsumesRequestCondition consumesCondition;
// @RequestMapping 中 produces 的解析器, 主要用于解析 HttpServletRequest 中的信息, 并進(jìn)行匹配
private final ProducesRequestCondition producesCondition;
// RequestCondition 的 holder
private final RequestConditionHolder customConditionHolder;
PS: 在進(jìn)行查找HttpServletRequest對應(yīng)的HandlerMethod 時(shí), 會通過 RequestMappingInfo.getMatchingCondition 來獲取與 HttpServletRequest & @RequestMapping 都匹配的 RequestMappingInfo, 有的話表示找到了需要的Handler
5. AbstractHandlerMapping 的子類 AbstractUrlHandlerMapping
相較于 AbstractHandlerMapping, AbstractUrlHandlerMapping中完成了
1. getHandlerInternal方法, Handler 的獲取(PS: 這里handler 可以是任意類型);
2. lookupHandler方法, 通過 urlPath, HttpServletRequest 獲取對應(yīng)的 Handler, 這里也包含了 "URI 模版變量" 的獲取(PS: 通過 AntPathMatcher.extractUriTemplateVariables)
3. registerHandler方法, 將 urlPath, handler 注冊到 handlerMap 中
getHandlerInternal中定義了獲取 Handler 的主邏輯, 若獲取不到且uri是"/"則使用 rootHandler, 否則使用 defaultHandler; 代碼如下:
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 從 Request 中得到 請求的 URL 路徑
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 將得到的 URL 路徑與 Handler 進(jìn)行匹配, 得到對應(yīng)的 Handler, 如果沒有對應(yīng)的 Handler, 返回 null, 這樣默認(rèn)的 Handler 會被使用
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
// 如果請求的路徑僅僅是 "/", 那么使用 RootHandler 進(jìn)行處理
rawHandler = getRootHandler();
}
if (rawHandler == null) {
// 無法找到 Handler, 則使用默認(rèn)的 Handler
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// 根據(jù) beanName 找到對應(yīng)的 bean
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
// 模板方法 校驗(yàn) hanlder 是否合法, 比如 DefaultAnnotationHandlerMapping 中校驗(yàn)類上面是否有 @RequestMapping 注解
validateHandler(rawHandler, request);
// 將 rawHandler HandlerInterceptor 包裝到 chain 中 <- 其中涉及到 暴露 URI 模版變量 <-- 就是 www.baidu.com/{gropuId}/{userId}/{pageNo} <-- 中 groupId, userId, pageNo 的值, 其實(shí)就是 @PathVariable 這個(gè)注解解析時(shí)用到的值
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}
但真正的通過 urlPath 來進(jìn)行直接匹配或通配符匹配是在 lookupHandler 的方法中, 并且方法中還有提取 uri 模版變量的步驟, 提取好后就直接存儲在 HttpServletRequest 中
// lookupHandler 根據(jù) URL 路徑啟動(dòng)在 handlerMap 中對 handler 的檢索, 并最終返回 handler對象
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接匹配情況的處理
// Direct match?
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) { // 若 handler 是 string 類型, 則將 handler 當(dāng)作類名, 直接從 BeanFactory 中獲取 Bean
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// 模板方法 校驗(yàn) hanlder 是否合法, 比如 DefaultAnnotationHandlerMapping 中校驗(yàn)類上面是否有 @RequestMapping 注解
validateHandler(handler, request);
// 將 rawHandler HandlerInterceptor 包裝到 chain 中 <- 其中涉及到 暴露 URI 模版變量 <-- 就是 www.baidu.com/{gropuId}/{userId}/{pageNo} <-- 中 groupId, userId, pageNo 的值, 其實(shí)就是 @PathVariable 這個(gè)注解解析時(shí)用到的值
return buildPathExposingHandler(handler, urlPath, urlPath, null); // <-- 最后一個(gè)參數(shù)是 null, 則 URI 模版參數(shù)就沒有了
}
// 通配符匹配的處理
// Pattern match?
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) { // 通過 AntPathMatcher 來進(jìn)行匹配 <-- 正則匹配
matchingPatterns.add(registeredPattern); // 匹配成功, 加入 matchingPatterns <-- matchingPatterns 里面可能有多個(gè)值
}
else if (useTrailingSlashMatch()) { // 是否使用尾部反斜桿進(jìn)行匹配, 若是的話, 則直接在尾部加上 "/" 再進(jìn)行匹配
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern +"/"); // 匹配成功, 加入 matchingPatterns <-- matchingPatterns 里面可能有多個(gè)值
}
}
}
String bestMatch = null; // 獲取 AntPatternComparator, 主要是處理多個(gè) urlPath 的排序
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) { // 通過 AntPatternComparator 進(jìn)行排序, 獲取排序的第一個(gè)值
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0); // 獲取匹配成功的第一個(gè)路徑
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch); // 從 handlerMap 從獲取 bestMatch 匹配的 handler
if (handler == null) {
if (bestMatch.endsWith("/")) { // 若獲取不到, 但 bestMatch 又是以 "/" 結(jié)尾的, 則去除 "/", 再從 handlerMap 里面獲取一次
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) throw new IllegalStateException("Could not find handler for best pattern match [" + bestMatch + "]");
}
// Bean name or resolved handler?
if (handler instanceof String) { // 若從 handlerMap 里面獲取出的 handler 是 String, 則再從 ApplicationContext 里面獲取對應(yīng)的 Bean
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request); // 模板方法 校驗(yàn) hanlder 是否合法, 比如 DefaultAnnotationHandlerMapping 中校驗(yàn)類上面是否有 @RequestMapping 注解
// 獲取 正則表達(dá)式中 *, ? 所代表的真實(shí)字符串
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them // 獲取 URI 模版變量的值
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
// 將 rawHandler HandlerInterceptor 包裝到 chain 中 <- 其中涉及到 暴露 URI 模版變量 <-- 就是 www.baidu.com/{gropuId}/{userId}/{pageNo} <-- 中 groupId, userId, pageNo 的值, 其實(shí)就是 @PathVariable 這個(gè)注解解析時(shí)用到的值
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
接著就是registerHandler, 將 url <--> handler 注冊到映射容器 handlerMap 中, 整個(gè)流程比較簡單, 若 urlPath 是 "/", 則設(shè)置為 rootHandler, 若 urlPath 是 "/*", 則設(shè)置為 defaultHandler, 其他的則直接設(shè)置到 handlerMap(PS: 從中我們也可以看出這個(gè) HandlerMapping 其實(shí)不是發(fā)安全的, 從 handlerMap 是個(gè) HashMap 可以看出)
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Object resolvedHandler = handler;
// 若 handler 是 String (BeanName), 是否需要將其初始化成 Bean
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
if (getApplicationContext().isSingleton(handlerName)) { // 從 ApplicationContext 拿出 Handler
resolvedHandler = getApplicationContext().getBean(handlerName);
}
}
// 是否已存在對應(yīng)的 handler, 若存在, 且里面存儲的 handler 與現(xiàn)在將注入進(jìn)去的不同, 則拋出異常
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException("Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else { // 不存在 handler
// 處理 URL 是 "/" 地映射, 把這個(gè) "/" 映射地 controller 設(shè)置到 rootHandler 中
if (urlPath.equals("/")) {
// "/" --> 設(shè)置為 rootHandler
setRootHandler(resolvedHandler);
}
// 處理 URL 是 "/*" 地映射, 把這個(gè) "/*" 映射地 controller 設(shè)置到 defaultHandler 中
else if (urlPath.equals("/*")) {
// 對 "/*" 的匹配 設(shè)置默認(rèn)的 handler
setDefaultHandler(resolvedHandler);
}
// 處理正常地 URL 映射, 設(shè)置 handlerMap 的 key 和 value, 分別對應(yīng) URL 和 映射的 controller
else {
// 其余 的路徑綁定關(guān)系則存入 handlerMap
this.handlerMap.put(urlPath, resolvedHandler);
}
}
}
6. AbstractUrlHandlerMapping 的子類 SimpleUrlHandlerMapping
SimpleUrlHandlerMapping 就像其名字一樣, 一個(gè)簡單的 HandlerMapping, 一般都是在xml文件中配置一個(gè), 配置的內(nèi)容主要是 mappings <-- 這里包含了 urlPath 與 handler 的映射關(guān)系, 下面是一個(gè)簡單的配置文件內(nèi)容
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
// 處理 /* 的handler
<property name="defaultHandler"><ref local="starController"/></property>
// 處理 / 的 handler
<property name="rootHandler"><ref local="mainController"/></property>
// 是否使用尾部斜桿匹配url
<property name="useTrailingSlashMatch" value="true"/>
<property name="urlMap">
<map>
<!-- 處理 /welcome 的是 welcomeController -->
<entry key="/welcome"><ref local="welcomeController"/></entry>
</map>
</property>
</bean>
7. AbstractUrlHandlerMapping 的子類AbstractDetectingUrlHandlerMapping
AbstractDetectingUrlHandlerMapping 從其名字中我們也可以看出, 這是一個(gè)自動(dòng)獲取 URL 的HandlerMapping, 同樣對應(yīng)的觸發(fā)還是通過 ApplicationContextAware.setApplicationContext 方法!
// 注冊所有在 ApplicationContext 中的 Handler
protected void detectHandlers() throws BeansException {
// 獲取 ApplicationContext 中所有的 beanNames
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// 根據(jù) beanName 獲取所有的 urls, 并注冊到 AbstractUrlHandlerMapping.handlerMap 中
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName); // 通過 beanName 獲取 url, 比如 BeanNameUrlHandlerMapping 是以 beanName 為 url <-- beanName 必需以 "/" 開頭
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
}
}
8. AbstractDetectingUrlHandlerMapping 的子類
1. BeanNameUrlHandlerMapping: 一個(gè)把beanName當(dāng)作urlPath的HandlerMapping(PS: BeanName必需以 "/" 開頭)
2. ControllerClassNameHandlerMapping: 基于 beanName 生成 uri 的 HandlerMapping(PS: 此類已經(jīng)廢棄)
3. ControllerBeanNameHandlerMapping: 基于 className 生成 uri 的 HandlerMapping(PS: 此類已經(jīng)廢棄)
4. DefaultAnnotationHandlerMapping: 掃描在 Bean 上注解 @RequestMapping|@Controller 的 類, 收集這個(gè)類中所有被 @RequetsMapping 注解修飾的方法, 并將 urlPaths <---> handlerType 注冊到AbstractUrlHandlerMapping.handlerMap中(PS: 不過此類也已經(jīng)廢棄)
9. HandlerMapping 中的優(yōu)秀設(shè)計(jì)
1. 模版模式: 在Spring中這是一個(gè)非常常見的設(shè)計(jì)模式, 比如 AbstractDetectingUrlHandlerMapping.detectHandlers, 其中定義了獲取handler的主邏輯, 但留下了獲取 url 的模版方法, 在其子類中進(jìn)行類相應(yīng)的實(shí)現(xiàn), 子類中有根據(jù) beanClass|beanName, 也有根據(jù)子類方法上的 @RequestMapping 注解
2. 建造者模式: HandlerMapping中主要體現(xiàn)在創(chuàng)建RequestMappingInfo的過程, 通過建造者 RequestMappingInfo.DefaultBuilder 來創(chuàng)建, 當(dāng)創(chuàng)建一個(gè)對象時(shí)需要很多屬性設(shè)置時(shí)就可以用 建造者模式
3. 策略模式: 這里的策略模式體現(xiàn)在 RequestCondition上(AbstractRequestCondition: 主要是將@RequestMapping上屬性與 HttpServletRequest進(jìn)行條件匹配, 最終找出兩者都符合的 RequestMappingInfo)
4. 組合模式: CompositeRequestCondition 將所有 RequestCondition組合起來, 構(gòu)成一個(gè) RequestCondition來進(jìn)行匹配操作
5. 責(zé)任鏈模式: 這個(gè)主要體現(xiàn)在 HandlerExecutionChain上, 鏈條的里面都是 interceptor, 最后執(zhí)行的才是 Handler
10. 總結(jié)
整個(gè) HandlerMapping 體系的設(shè)計(jì)融合了常見的設(shè)計(jì)模式以及Spring IOC中的擴(kuò)展接口 ApplicationContextAware, 完成了 urlPath 與 handler 之間映射的注冊, 搜索工作!
11. 參考資料
SpringMVC源碼分析系列
Spring MVC源碼剖析
Spring源碼情操陶冶
Spring 揭秘
Spring 技術(shù)內(nèi)幕
Spring 源碼深度分析
看透 Spring MVC 源代碼分析與實(shí)踐