SpringMVC 4.3 源碼分析之 HandlerMapping

1. HandlerMapping 概述

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


HandlerMapping.png
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í)踐

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,253評論 6 342
  • 什么是Spring Spring是一個(gè)開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,764評論 1 133
  • 半水微風(fēng)半水瀾,半江蘆葦倚風(fēng)諳。閑人半坐執(zhí)竿釣,半似望魚半似眠。
    月夕華閱讀 181評論 0 0
  • 上一篇只是主要說了springboot啟動(dòng)流程。剛剛想起來里面的tomcat啥時(shí)候啟動(dòng)的,還有設(shè)置applicat...
    lijiaccy閱讀 3,147評論 1 3

友情鏈接更多精彩內(nèi)容