SpringMVC源碼解析三(處理映射器HandlerMapping的解析)

HandlerMapping繼承關係圖:

1.BeanNameUrlHandlerMapping

配置文件中配置controller的bean,且bean的id必須以“/”開頭,id就是controller可以處理的url

<bean id = "/hello" class="com.liyao.controller.HelloController"/>

2.SimpleUrlHandlerMapping

需要在配置文件中配置controller的bean以及mapping

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hi">hi</prop>
            </props>
        </property>
    </bean>
<bean id = "hi" class="com.lic.controller.HiController"/>

3.RequestMappingUrlHandlerMapping

基於@RequestMapping註解對應映射

AbstractUrlHandlerMapping 映射器映射出來的是 handler 是 Controller 對象, 也就是類級別的處理器

AbstractHandlerMethodMapping 映射器映射出來的 handler 是 HandlerMethod 對象, 也就是方法級別的處理器

DispatcherServlet#getHandler()方法實現:

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        //遍歷所有HandlerMapping, 獲取適合當前請求的映射處理器
        for (HandlerMapping hm : this.handlerMappings) {
            if (logger.isTraceEnabled()) {
                logger.trace("Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
            }
            /**
             * 1.遍歷所有的HandlerMapping,根據Request獲取到合適的HandlerMapping
             * 2.利用HandlerMapping通過請求信息獲取Handler處理器幷包裝爲HandlerExecutionChain執行鏈
             *
             * {@link AbstractHandlerMapping#getHandler(HttpServletRequest)}
             * hm  ==>  RequestMappingInfoHandlerMapping
             */
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

分析

  1. 遍歷所有的註冊HandlerMapping,根據Request獲取到合適的HandlerMapping
  2. 利用HandlerMapping通過請求信息獲取Handler處理器幷包裝爲HandlerExecutionChain執行鏈返回

AbstractHandlerMapping#getHandler()方法實現:

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    /**
     * 由子類實現, 根據請求信息獲取指定的Handler
     * {@link AbstractHandlerMethodMapping#getHandlerInternal(HttpServletRequest)}
         * {@link AbstractUrlHandlerMapping#getHandlerInternal(javax.servlet.http.HttpServletRequest)}
     */
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
    // Bean name or resolved handler?
    if (handler instanceof String) {
        String handlerName = (String) handler;
        //從容器中獲取Handler
        handler = obtainApplicationContext().getBean(handlerName);
    }
    /**
     * 將handler和request包裝爲HandlerExecutionChain,在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;
}

分析:

  1. AbstractHandlerMapping子類中根據請求信息獲取指定的Handler
  2. 如果獲取的handler是一個String類型的名稱, 則從容器中獲取handler實例
  3. 將handler和request包裝爲HandlerExecutionChain,在HandlerExecutionChain設置了相關過濾器

1. AbstractHandlerMapping子類一: AbstractHandlerMethodMapping

AbstractHandlerMethodMapping#getHandlerInternal()方法實現:

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    //lookupPath爲請求路徑信息
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    if (logger.isDebugEnabled()) {
        logger.debug("Looking up handler method for path " + lookupPath);
    }
    this.mappingRegistry.acquireReadLock();  //獲取讀鎖
    try {
        /**
         * 1. 獲取HandlerMethod實例(不健全, 數據沒有封裝完成)
         */
        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 + "]");
            }
        }
        /**
         * 2. 重塑HandlerMethod實例  ==> 重點
         */
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

分析:

  1. 根據請求信息獲取HandlerMethod實例
  2. 封裝HandlerMethod實例

(1). AbstractHandlerMethodMapping#lookupHandlerMethod()方法實現:

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    /**
      * 由映射處理器映射出對應的MappingInfo信息, 可能獲取多個
      */
    List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
    if (directPathMatches != null) {
        /**
         * 將HandlerMethod實例與Request進行包裝, 添加到matches集合中
         */
        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);
        }
        /**
         * 獲取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) {
                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);
        /**
         * {@link RequestMappingInfoHandlerMapping#handleMatch(org.springframework.web.servlet.mvc.method.RequestMappingInfo, String, HttpServletRequest)}
         */
        handleMatch(bestMatch.mapping, lookupPath, request);
        return bestMatch.handlerMethod;
    }
    else {
        return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
    }
}

分析: 

  1. 根據url從註冊中心獲取對應的MappingInfo信息,封裝到directPathMatches集合中
  2. 將HandlerMethod實例與Request進行包裝, 並且創建Match實例, 在該實例中封裝了HandlerMethod方法
  3. 由映射處理器(RequestMappingInfoHandlerMapping) 映射出對應的handler

AbstractHandlerMethodMapping#addMatchingMappings()方法實現:

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
    for (T mapping : mappings) {
        T match = getMatchingMapping(mapping, request);
        if (match != null) {
            /**
             * 根據請求路徑從mappingLookup集合中獲取處理方法, 將Method封裝到Match實例中
             * 後面處理請求時會利用反射執行該方法
             */
            matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
        }
    }
}

(2) HandlerMethod#createWithResolvedBean()方法實現:

public HandlerMethod createWithResolvedBean() {
    Object handler = this.bean;
    if (this.bean instanceof String) {  //如果bean不是實例而是一個BeanName,則從工廠中獲取該bean實例
        Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
        String beanName = (String) this.bean;
        // 從工廠中獲取bean
        handler = this.beanFactory.getBean(beanName);
    }
    /**
     * 問題: 這裏爲什麼要重新創建一個HandlerMethod實例,而不是在原有的HandlerMethod實例上進行修改?
     */
    return new HandlerMethod(this, handler);
}

private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
    Assert.notNull(handlerMethod, "HandlerMethod is required");
    Assert.notNull(handler, "Handler object is required");
    this.bean = handler;  //反射執行Method時需要傳入實例對象
    this.beanFactory = handlerMethod.beanFactory;
    this.beanType = handlerMethod.beanType;
    this.method = handlerMethod.method;
    this.bridgedMethod = handlerMethod.bridgedMethod;
    this.parameters = handlerMethod.parameters;
    this.responseStatus = handlerMethod.responseStatus;
    this.responseStatusReason = handlerMethod.responseStatusReason;
    this.resolvedFromHandlerMethod = handlerMethod;
}

這裏有一個疑問: 爲什麼要重新創建一個HandlerMethod實例,而不是在原有的HandlerMethod實例上進行修改? 

1.1 AbstractHandlerMethodMapping子類: RequestMappingInfoHandlerMapping

在容器初始化過程中創建映射器(RequestMappingHandlerMapping)對象時,會尋找所有被@Controller 註解類中被 @RequestMapping 註解的方法,然後解析方法上的 @RequestMapping 註解,把解析結果封裝成 RequestMappingInfo 對象,也就是說RequestMappingInfo 對象是用來裝載方法的匹配相關信息,每個匹配的方法都會對應一個 RequestMappingInfo 對象

RequestMappingInfoHandlerMapping#handleMatch()方法實現: 

@Override
protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
    super.handleMatch(info, lookupPath, request);

    String bestPattern;
    Map<String, String> uriVariables;

    Set<String> patterns = info.getPatternsCondition().getPatterns();
    if (patterns.isEmpty()) {
        bestPattern = lookupPath;
        uriVariables = Collections.emptyMap();
    }
    else {
        bestPattern = patterns.iterator().next();
        uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
    }

    request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);

    if (isMatrixVariableContentAvailable()) {
        Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
        request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
    }

    Map<String, String> decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
    request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);

    if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
        Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
        request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
    }
}

MappingRegistry#getMappingsByUrl()方法實現:

@Nullable
public List<T> getMappingsByUrl(String urlPath) {
    return this.urlLookup.get(urlPath);
}

由此可知, 在初始化SpringMVC時處理器已經註冊到urlLookup集合中, key爲url, 在MappingRegistry#register()方法中實現了對處理器的註冊

AbstractHandlerMethodMapping.MappingRegistry#register()方法實現

public void register(T mapping, Object handler, Method method) {
    this.readWriteLock.writeLock().lock();
    try {
        HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        assertUniqueMethodMapping(handlerMethod, mapping);
        /**
         * mappingLookup: 維護請求信息(RequestMappingInfo)與HandlerMethod的映射關係
         */
        this.mappingLookup.put(mapping, handlerMethod);

        if (logger.isInfoEnabled()) {
            logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
        }

        List<String> directUrls = getDirectUrls(mapping);
        for (String url : directUrls) {
            /**
             * urlLookup: 維護url與請求信息(RequestMappingInfo)的映射關係
             * 後面會根據Url找RequestMappingInfo, 再根據RequestMappingInfo找HandlerMethod對請求進行處理
             */    
            this.urlLookup.add(url, mapping);
        }

        String name = null;
        if (getNamingStrategy() != null) {
            name = getNamingStrategy().getName(handlerMethod, mapping);
            addMappingName(name, handlerMethod);
        }

        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();
    }
}

在this.urlLookup.add(url, mapping) 處添加斷點查看調用棧:

分析:

  1. 模塊1主要邏輯爲創建以及配置Web應用上下文(WebApplicationContext)
  2. 模塊2主要邏輯爲springMVC相關bean對象註冊解析, 即WebApplicationContext的實例化
  3. 模塊3主要邏輯爲遍歷所有Controller類, 對類中定義的所有處理方法進行註冊,即註冊Handler處理器

模塊3邏輯分析:

RequestMappingInfoHandlerMapping類實現了InitializingBean接口並重寫了afterPropertiesSet(), 在調用初始化方法時, 會觸發afterPropertiesSet()方法, 將啓動對處理器(handler)的探測以及註冊

AbstractHandlerMethodMapping#afterPropertiesSet()方法實現:

@Override
public void afterPropertiesSet() {
    //初始化Handler方法, 就是對編寫的Control層方法進行註冊, key爲方法的訪問路徑, value爲對方法的包裝類HandlerMethod
    initHandlerMethods();
}

protected void initHandlerMethods() {
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for request mappings in application context: " + getApplicationContext());
    }
    String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
            obtainApplicationContext().getBeanNamesForType(Object.class));

    for (String beanName : beanNames) {
        if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            Class<?> beanType = null;
            try {
                //獲取對應bean的Class
                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);
                }
            }
            /**
             * isHandler()方法用來判斷是否符合註冊條件, 具體的判斷邏輯由子類實現
             */
            if (beanType != null && isHandler(beanType)) {
                //對Controller類中所有的方法進行探測
                detectHandlerMethods(beanName);
            }
        }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

AbstractHandlerMethodMapping#detectHandlerMethods()方法實現:

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);
                    }
                    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) -> {
            /**
             * 循環遍歷所有的方法,進行註冊
             * handler爲controller類實例, 每一個HandlerMethod中都維護着該實例, 因爲在後面執行該方法時需要傳入該實例
             */
            Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
            registerHandlerMethod(handler, invocableMethod, mapping);
        });
    }
}

 

2.AbstractHandlerMapping子類二: AbstractUrlHandlerMapping

AbstractUrlHandlerMapping#getHandlerInternal()方法實現:

@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    //獲取請求路徑
    String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    /**
     * 根據請求路徑獲取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)) {
            rawHandler = getRootHandler();
        }
        if (rawHandler == null) {
            rawHandler = getDefaultHandler();
        }
        if (rawHandler != null) {
            // Bean name or resolved handler?
            if (rawHandler instanceof String) {
                String handlerName = (String) rawHandler;
                rawHandler = obtainApplicationContext().getBean(handlerName);
            }
            validateHandler(rawHandler, request);
            handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
        }
    }
    if (handler != null && logger.isDebugEnabled()) {
        logger.debug("Mapping [" + lookupPath + "] to " + handler);
    }
    else if (handler == null && logger.isTraceEnabled()) {
        logger.trace("No handler mapping found for [" + lookupPath + "]");
    }
    return handler;
}

AbstractUrlHandlerMapping#lookupHandler()方法實現:

@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
    //嘗試直接匹配
    Object handler = this.handlerMap.get(urlPath);
    if (handler != null) {
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }
        validateHandler(handler, request);
        return buildPathExposingHandler(handler, urlPath, urlPath, null);
    }

    //嘗試模式匹配
    List<String> matchingPatterns = new ArrayList<>();
    for (String registeredPattern : this.handlerMap.keySet()) {
        if (getPathMatcher().match(registeredPattern, urlPath)) {
            matchingPatterns.add(registeredPattern);
        }
        else if (useTrailingSlashMatch()) {
            if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
                matchingPatterns.add(registeredPattern + "/");
            }
        }
    }

    String bestMatch = null;
    Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
    if (!matchingPatterns.isEmpty()) {
        matchingPatterns.sort(patternComparator);
        if (logger.isDebugEnabled()) {
            logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
        }
        bestMatch = matchingPatterns.get(0);
    }
    if (bestMatch != null) {
        handler = this.handlerMap.get(bestMatch);
        if (handler == null) {
            if (bestMatch.endsWith("/")) {
                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) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }
        validateHandler(handler, request);
        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
        Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
        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);
            }
        }
        if (logger.isDebugEnabled()) {
            logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
        }
        return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
    }

    // No handler found...
    return null;
}

在上面方法中都是通過handlerMap獲取指定handler, 那麼接下來就看下怎麼將handler對象加入handlerMap集合中

AbstractUrlHandlerMapping#registerHandler()方法實現:

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
    Assert.notNull(urlPath, "URL path must not be null");
    Assert.notNull(handler, "Handler object must not be null");
    Object resolvedHandler = handler;

    // Eagerly resolve handler if referencing singleton via name.
    if (!this.lazyInitHandlers && handler instanceof String) {
        String handlerName = (String) handler;
        ApplicationContext applicationContext = obtainApplicationContext();
        if (applicationContext.isSingleton(handlerName)) {
            resolvedHandler = applicationContext.getBean(handlerName);
        }
    }

    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 {
        if (urlPath.equals("/")) {
            if (logger.isInfoEnabled()) {
                logger.info("Root mapping to " + getHandlerDescription(handler));
            }
            setRootHandler(resolvedHandler);
        }
        else if (urlPath.equals("/*")) {
            if (logger.isInfoEnabled()) {
                logger.info("Default mapping to " + getHandlerDescription(handler));
            }
            setDefaultHandler(resolvedHandler);
        }
        else {
            /**
             * 此處完成了對Handler的註冊
             */
            this.handlerMap.put(urlPath, resolvedHandler);
            if (logger.isInfoEnabled()) {
                logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
            }
        }
    }
}

在AbstractUrlHandlerMapping#registerHandler()方法中完成了對handler的註冊

查看該方法的調用棧:

通過調用棧可知, 是在子類中調用父類AbstractUrlHandlerMapping的registerHandler()方法

 

2.1 AbstractUrlHandlerMapping子類一: SimpleUrlHandlerMapping

SimpleUrlHandlerMapping#registerHandlers()方法實現:

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
    if (urlMap.isEmpty()) {
        logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
    }
    else {
        urlMap.forEach((url, handler) -> {
            // Prepend with slash if not already present.
            if (!url.startsWith("/")) {
                url = "/" + url;
            }
            // Remove whitespace from handler bean name.
            if (handler instanceof String) {
                handler = ((String) handler).trim();
            }
            registerHandler(url, handler);
        });
    }
}

在SimpleUrlHandlerMapping#registerHandlers(Map<String, Object> urlMap)中調用了SimpleUrlHandlerMapping#registerHandlers(

String urlPath, Object handler)方法

SimpleUrlHandlerMapping#initApplicationContext()方法實現:

@Override
public void initApplicationContext() throws BeansException {
       /**
     * 其頂級父類AbstractHandlerMapping繼承了WebApplicationObjectSupport,容器初始化時會自動調用模板方法
     * initApplicationContext, 這裏子類重寫該方法並加了一些邏輯, 主要是爲了將匹配該映射器的Handler對象加入
     * 到handlerMap集合中
     */
    super.initApplicationContext();
    registerHandlers(this.urlMap);
}

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
    if (urlMap.isEmpty()) {
        logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
    }
    else {
        urlMap.forEach((url, handler) -> {
            // Prepend with slash if not already present.
            if (!url.startsWith("/")) {
                url = "/" + url;
            }
            // Remove whitespace from handler bean name.
            if (handler instanceof String) {
                handler = ((String) handler).trim();
            }
            registerHandler(url, handler);
        });
    }
}

 

2.2 AbstractUrlHandlerMapping子類二: BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping中沒有registerHandlers()方法, 而是由其父類AbstractUrlHandlerMapping進行實現

AbstractDetectingUrlHandlerMapping#initApplicationContext()方法實現:

@Override
public void initApplicationContext() throws ApplicationContextException {
    /**
     * 其頂級父類AbstractHandlerMapping繼承了WebApplicationObjectSupport,容器初始化時會自動調用模板方法
     * initApplicationContext, 這裏子類重寫該方法並加了一些邏輯, 主要是爲了將匹配該映射器的Handler對象加入
     * 到handlerMap集合中
     */
    super.initApplicationContext();
    detectHandlers();
}
protected void detectHandlers() throws BeansException {
    ApplicationContext applicationContext = obtainApplicationContext();
    if (logger.isDebugEnabled()) {
        logger.debug("Looking for URL mappings in application context: " + applicationContext);
    }
    /**
     * 將SpringMVC容器中註冊的Bean的name都找出來放進數組中
     */
    String[] beanNames = (this.detectHandlersInAncestorContexts ?
            BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
            applicationContext.getBeanNamesForType(Object.class));

    // Take any bean name that we can determine URLs for.
    for (String beanName : beanNames) {
        /**
         * 匹配到符合該映射器的bean的name
         */
        String[] urls = determineUrlsForHandler(beanName);
        if (!ObjectUtils.isEmpty(urls)) {
            /**
             * 調用父類(AbstractUrlHandlerMapping)的registerHandler方法將匹配的beanName和urls分別作爲key和value
             * 裝進handlerMap集合中
             */
            registerHandler(urls, beanName);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
            }
        }
    }
}

BeanNameUrlHandlerMapping#determineUrlsForHandler方法實現:

@Override
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);
}

至此, 處理器的獲取解析完成;

 

相關文章:

     SpringMVC源碼解析一(在Spring源碼項目中搭建SpringMVC源碼模塊)

     SpringMVC源碼解析二(請求過程解析)

     SpringMVC源碼解析三(處理映射器HandlerMapping的解析)

     SpringMVC源碼解析四(處理適配器HandlerAdapter的解析)

     SpringMVC源碼解析五(HandlerMethod執行過程解析)

     SpringMVC源碼解析六(ModelAndView解析)

     SpringMVC源碼解析七(初始化過程解析

發佈了96 篇原創文章 · 獲贊 102 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章