SpringMVC源碼解析(二)——HandlerMapping

前言

    上一篇涉及的許多例如容器實例化、容器刷新等,在以前Spring源碼解析中都已講解過,再也不贅述。本節咱們來分析下 HandlerMapping 的初始化,這裏的初始化工做將對咱們以後的請求映射起到關鍵做用。java

 

源碼解讀

    咱們首先來看下接口 HandlerMapping ,接口只定義一個方法,經過 request 請求返回一個 HandlerExecutionChain 對象。web

public interface HandlerMapping {

    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

    接着來看下 HandlerExecutionChain 裏面有哪些屬性。spring

public class HandlerExecutionChain {
    // 處理器對象
    private final Object handler;
    // 攔截器數組
    private HandlerInterceptor[] interceptors;

    public Object getHandler() {
        return this.handler;
    }
    
    // 遍歷執行攔截器
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerInterceptor[] interceptors = getInterceptors();
        if (!ObjectUtils.isEmpty(interceptors)) {
            for (int i = 0; i < interceptors.length; i++) {
                HandlerInterceptor interceptor = interceptors[i];
                if (!interceptor.preHandle(request, response, this.handler)) {
                    triggerAfterCompletion(request, response, null);
                    return false;
                }
                this.interceptorIndex = i;
            }
        }
        return true;
    }
}

    這裏以 applyPreHandle 爲例,還有 applyPostHandletriggerAfterCompletion,分別遍歷調用攔截器的 postHandle 和 afterCompletion。跨域

    咱們大體能夠分析出,HandlerMapping 就是根據請求 request,獲取到對應的請求執行鏈。準備知識事後,讓咱們來看看 HandlerMapping 的初始化邏輯:數組

public class DispatcherServlet extends FrameworkServlet {
    
    // 存放加載的 HandlerMapping實現
    private List<HandlerMapping> handlerMappings;

    private boolean detectAllHandlerMappings = true;

    // 銜接上一節
    @Override
    protected void onRefresh(ApplicationContext context) {
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        ......
        // 關注該方法:HandlerMapping 的初始化
        initHandlerMappings(context);
        ......
    }

    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;

        // 可指定,默認爲 true(即探測全部實現了HandlerMapping接口的 bean)
        if (this.detectAllHandlerMappings) {
            // beansOfTypeIncludingAncestors:經過 getBeansOfType獲取子容器和父容器內的 HandlerMapping
            // getBeansOfType會首先調用 getBeanNamesForType獲取指定類型的全部 beanName
            // 而後遍歷這些 beanName,使用 getBean建立實例
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
                // 對於實現了 PriorityOrdered、Ordered的 HandleMapping 排序
                // 若是沒有實現這倆接口,默認優先級最低
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        } else {
            try {
                // 獲取 beanName爲「handlerMapping」的 HandlerMapping
                HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
                this.handlerMappings = Collections.singletonList(hm);
            } catch (NoSuchBeanDefinitionException ex) {
                // Ignore, we'll add a default HandlerMapping later.
            }
        }

        // 若是上述處理後未找到 HandlerMapping
        if (this.handlerMappings == null) {
            // 默認策略:見 DispatcherServlet.properties(存儲了缺失的默認初始化類型)
            // 會註冊 BeanNameUrlHandlerMapping和 DefaultAnnotationHandlerMapping
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            if (logger.isDebugEnabled()) {
                logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
            }
        }
    }

    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
        // 按傳入 HandlerMapping舉例,key = org.springframework.web.servlet.HandlerMapping
        String key = strategyInterface.getName();
        // 獲取對應的值:org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
        // org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
        String value = defaultStrategies.getProperty(key);
        if (value != null) {
            // 根據逗號分割
            String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
            List<T> strategies = new ArrayList<T>(classNames.length);
            for (String className : classNames) {
                try {
                    Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
                    // 調用 createBean建立該實例
                    Object strategy = createDefaultStrategy(context, clazz);
                    strategies.add((T) strategy);
                } ...// 省略 catch
            }
            return strategies;
        } else {
            return new LinkedList<T>();
        }
    }
}

    這一步的初始化,默認的邏輯就將探測到全部 HandleMapping 類型的實例,賦值給成員變量 handlerMappings。看似好像沒有很複雜的邏輯,其實背後隱藏了不少細節。restful

    這裏咱們首先要介紹一個類,ApplicationObjectSupport mvc

public abstract class ApplicationObjectSupport implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public final void setApplicationContext(ApplicationContext context) throws BeansException {
        if (context == null && !isContextRequired()) {
            this.applicationContext = null;
            this.messageSourceAccessor = null;
        } else if (this.applicationContext == null) {
            // ApplicationContext類型檢測
            if (!requiredContextClass().isInstance(context)) {
                throw new ApplicationContextException(
                        "Invalid application context: needs to be of type [" +
                                requiredContextClass().getName() + "]");
            }
            // 賦值,用於判斷是否重複初始化(及父子容器初始化,該方法也只會執行一次)
            this.applicationContext = context;
            this.messageSourceAccessor = new MessageSourceAccessor(context);
            // 擴展點
            initApplicationContext(context);
        } else {
            // Ignore reinitialization if same context passed in.
            if (this.applicationContext != context) {
                throw new ApplicationContextException(
                        "Cannot reinitialize with different application context: current one is ["
                                + this.applicationContext + "], passed-in one is [" + context + "]");
            }
        }
    }

    protected void initApplicationContext(ApplicationContext context) throws BeansException {
        initApplicationContext();
    }

    /**
     * 被子類擴展以實現自定義的初始化邏輯
     */
    protected void initApplicationContext() throws BeansException {
    }
}

    咱們來分析一下 setApplicationContext 調用時機,首先該方法來自 ApplicationContextAware,該方法是在 ApplicationContextAwareProcessor.invokeAwareInterfaces 被調用。app

class ApplicationContextAwareProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        .....// 省略 AccessControlContext的獲取
        if (acc != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                @Override
                public Object run() {
                    invokeAwareInterfaces(bean);
                    return null;
                }
            }, acc);
        } else {
            invokeAwareInterfaces(bean);
        }
        return bean;
    }

    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            .....// 省卻其餘一些 Aware的調用
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }
}

    省略了一些代碼,能夠看出是在 Bean 的生命週期接口方法 BeanPostProcessor.postProcessBeforeInitialization 被調用的。cors

    而 ApplicationContextAwareProcessor 的註冊就要追溯到「 容器刷新 」的 prepareBeanFactory 方法了。框架

    繞了一圈,咱們發現,其實 Spring 的實現隱藏在了許多細節裏,這樣的實現讓代碼的耦合程度很是低,極易將邏輯封裝到各個模塊中。

    咱們再回到以前提到的 ApplicationObjectSupport ,這個類提供了 initApplicationContext 可供子類實現定製化的邏輯。

 

AbstractHandlerMapping

// WebApplicationObjectSupport爲 ApplicationObjectSupport子類
public abstract class AbstractHandlerMapping 
                   extends WebApplicationObjectSupport implements HandlerMapping, Ordered {

    // 這一步主要是攔截器的相關處理
    @Override
    protected void initApplicationContext() throws BeansException {
        // 空實現
        extendInterceptors(this.interceptors);
        // 探測全部 MappedInterceptor填充 adaptedInterceptors
        // <mvc:interceptors>下的一個個 <mvc:interceptor>就會被封裝成 MappedInterceptor
        detectMappedInterceptors(this.adaptedInterceptors);

        initInterceptors();
    }

    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
        mappedInterceptors.addAll(
                BeanFactoryUtils.beansOfTypeIncludingAncestors(
                        getApplicationContext(), MappedInterceptor.class, true, false).values());
    }
}

    AbstractHandlerMapping 做爲 HandlerMapping 類型的頂層抽象基類,自身實現了攔截器的初始化工做。子類劃分兩大分支:AbstractUrlHandlerMapping 和 AbstractHandlerMethodMapping

 

AbstractUrlHandlerMapping 分支

// AbstractUrlHandlerMapping爲 AbstractHandlerMapping子類
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {

    // 默認不從父容器查找「請求處理器」
    private boolean detectHandlersInAncestorContexts = false;

    @Override
    public void initApplicationContext() throws ApplicationContextException {
        super.initApplicationContext();
        // 探測 Handler的邏輯
        detectHandlers();
    }

    protected void detectHandlers() throws BeansException {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
        }
        // 默認只在子容器中查找,首先拿到全部註冊的 beanName
        String[] beanNames = (this.detectHandlersInAncestorContexts ?
           BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class));

        for (String beanName : beanNames) {
            // 會將能做爲 Handler實例對應的 urls返回
            String[] urls = determineUrlsForHandler(beanName);
            if (!ObjectUtils.isEmpty(urls)) {
                // 註冊 urls和 Handler的映射關係
                registerHandler(urls, beanName);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
                }
            }
        }
    }
}

    AbstractDetectingUrlHandlerMapping 以模板方法,將挑選請求處理器以抽象方法 determineUrlsForHandler 留給子類實現不一樣策略,自身經過調用父類 registerHandler 實現了處理器的註冊邏輯。

public abstract class AbstractUrlHandlerMapping 
                        extends AbstractHandlerMapping implements MatchableHandlerMapping {

    protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
        Assert.notNull(urlPaths, "URL path array must not be null");
        for (String urlPath : urlPaths) {
            registerHandler(urlPath, beanName);
        }
    }

    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;

        // 將 beanName經過 getBean轉換成單例對象
        if (!this.lazyInitHandlers && handler instanceof String) {
            String handlerName = (String) handler;
            if (getApplicationContext().isSingleton(handlerName)) {
                resolvedHandler = getApplicationContext().getBean(handlerName);
            }
        }

        // handlerMap維護 url和 Handler的關聯關係
        Object mappedHandler = this.handlerMap.get(urlPath);
        // 確保一個 url僅能對應惟一的處理器
        if (mappedHandler != null) {
            if (mappedHandler != resolvedHandler) {
                ....// 拋異常,指明一個 url只能對應一個處理器
            }
        } else {
            if (urlPath.equals("/")) {
                if (logger.isInfoEnabled()) {
                    logger.info("Root mapping to " + getHandlerDescription(handler));
                }
                // 將 url爲「/」的設置根處理器
                setRootHandler(resolvedHandler);
            } else if (urlPath.equals("/*")) {
                if (logger.isInfoEnabled()) {
                    logger.info("Default mapping to " + getHandlerDescription(handler));
                }
                // 將 url爲「/*」的設置默認處理器
                setDefaultHandler(resolvedHandler);
            } else {
                // 其他的存儲關聯關係
                this.handlerMap.put(urlPath, resolvedHandler);
                if (logger.isInfoEnabled()) {
                    logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
                }
            }
        }
    }
}

    註冊就是將 url 數組和對應的 Handler 關聯起來。那麼什麼樣的實例才能做爲 Handler 呢?接下來咱們看看不一樣子類實現 determineUrlsForHandler 是如何獲取 Handler對應的 urls

 

BeanNameUrlHandlerMapping

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

    /**
     * beanName或 別名是以「/」開頭的.
     */
    @Override
    protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList<String>();
        if (beanName.startsWith("/")) {
            urls.add(beanName);
        }
        String[] aliases = getApplicationContext().getAliases(beanName);
        for (String alias : aliases) {
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }
        return StringUtils.toStringArray(urls);
    }
}

    這種最爲簡單,就是把 beanName、alias 以「/」開頭的實例看成 Handler,而後 urls 就是這些實例的 beanName、alias(別名)。

 

DefaultAnnotationHandlerMapping

@Deprecated
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {
    
    // 存放被 @RequestMapping標識的類,和註解的映射關係
    private final Map<Class<?>, RequestMapping> cachedMappings = new HashMap<Class<?>, RequestMapping>();


    /**
     * 這個方法會返回指定 beanName下組裝好的全部 urls
     * 組裝即類級別和方法級別 @RequestMapping value的組合
     * 若是類未被 @Controller或 @RequestMapping標識,返回 null
     */
    @Override
    protected String[] determineUrlsForHandler(String beanName) {
        ApplicationContext context = getApplicationContext();
        // 獲取指定 beanName的 Class類型
        Class<?> handlerType = context.getType(beanName);
        // 判斷該類是否被 @RequestMapping標識
        RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);

        // 這裏處理的是類級別的 @RequestMapping
        if (mapping != null) {
            this.cachedMappings.put(handlerType, mapping);
            Set<String> urls = new LinkedHashSet<String>();
            // 獲取類級別 @RequestMapping的 value
            String[] typeLevelPatterns = mapping.value();
            if (typeLevelPatterns.length > 0) {
                // 這一步會返回全部方法級別 @RequestMapping的 value全集
                String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
                for (String typeLevelPattern : typeLevelPatterns) {
                    // 若是定義的 url前沒有「/」,自動添加
                    if (!typeLevelPattern.startsWith("/")) {
                        typeLevelPattern = "/" + typeLevelPattern;
                    }
                    boolean hasEmptyMethodLevelMappings = false;
                    for (String methodLevelPattern : methodLevelPatterns) {
                        if (methodLevelPattern == null) {
                            hasEmptyMethodLevelMappings = true;
                        } else {
                            // 將類指定 url和方法指定 url拼接
                            String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
                            addUrlsForPath(urls, combinedPattern);
                        }
                    }
                    // 若是方法級別 @RequestMapping的 value爲空 或 類自己爲 Controller子類
                    if (hasEmptyMethodLevelMappings ||
                            org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
                        addUrlsForPath(urls, typeLevelPattern);
                    }
                }
                return StringUtils.toStringArray(urls);
            } else {
                // 類級別 @RequestMapping的 value爲空,直接處理方法級別 @RequestMapping
                return determineUrlsForHandlerMethods(handlerType, false);
            }
        }

        // 若是該類沒有被 @RequestMapping標識,但被 @Controller標識
        else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
            // 直接處理方法級別 @RequestMapping
            return determineUrlsForHandlerMethods(handlerType, false);
        }

        // 該類既沒被 @RequestMapping標識,也沒被 @Controller標識
        else {
            return null;
        }
    }


    protected String[] determineUrlsForHandlerMethods(Class<?> handlerType,
                                                      final boolean hasTypeLevelMapping) {
        String[] subclassResult = determineUrlsForHandlerMethods(handlerType);
        if (subclassResult != null) {
            return subclassResult;
        }

        final Set<String> urls = new LinkedHashSet<String>();
        Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
        handlerTypes.add(handlerType);
        handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
        // 遍歷自身和全部父接口
        for (Class<?> currentHandlerType : handlerTypes) {

            // 參數一:須要掃描的對象
            // 參數二:回調實現,會將 currentHandlerType下的全部方法(以及父類),傳入 doWith處理
            // 參數三:過濾匹配規則的方法。過濾掉橋接方法以及 Object類下的全部方法
            ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
                @Override
                public void doWith(Method method) {
                    // 全部被 @RequestMapping接口標記的方法
                    RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
                    if (mapping != null) {
                        // 獲取 @RequestMapping的 value
                        String[] mappedPatterns = mapping.value();
                        if (mappedPatterns.length > 0) {
                            // 遍歷 value數組
                            for (String mappedPattern : mappedPatterns) {
                                // 沒有類級別 @RequestMapping,且方法指定的 url沒有以「/」開頭,默認加上
                                if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) {
                                    mappedPattern = "/" + mappedPattern;
                                }
                                // 放入 url列表
                                addUrlsForPath(urls, mappedPattern);
                            }
                        } else if (hasTypeLevelMapping) {
                            // 若是方法 @RequestMapping的 value爲空
                            urls.add(null);
                        }
                    }
                }
            }, ReflectionUtils.USER_DECLARED_METHODS);
        }
        return StringUtils.toStringArray(urls);
    }


    protected void addUrlsForPath(Set<String> urls, String path) {
        urls.add(path);
        if (this.useDefaultSuffixPattern && path.indexOf('.') == -1 && !path.endsWith("/")) {
            urls.add(path + ".*");
            urls.add(path + "/");
        }
    }
}

    這是 Spring 3.2 版本以前對於註解標識 Handler的支持類。從上面的源碼,咱們能夠看出,處理是區分類級別註解、方法級別註解,在類上聲明的註解屬性將做用於該類全部的接口方法(最後會調用方法將二者合併)。最後 determineUrlsForHandler 返回的將會是組裝好的 url 全集。

    如今這個類已被標識廢棄,代替者爲 RequestMappingHandlerMapping。在瞭解它以前,咱們須要瞭解另外一分支,也是 RequestMappingHandlerMapping 的抽象父類。

 

AbstractHandlerMethodMapping

public abstract class AbstractHandlerMethodMapping<T> 
                           extends AbstractHandlerMapping implements InitializingBean {

    private boolean detectHandlerMethodsInAncestorContexts = false;

    // 內部類,用於註冊 url和 HandlerMethod的映射關係
    private final MappingRegistry mappingRegistry = new MappingRegistry();

    @Override
    public void afterPropertiesSet() {
        initHandlerMethods();
    }

    protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }

        // 獲取子容器中全部 Object類型的 beanName
        // detectHandlerMethodsInAncestorContexts默認爲 false,即只探測子容器(mvc)
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class));

        // 遍歷註冊的 beanName
        for (String beanName : beanNames) {
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class<?> beanType = null;
                try {
                    beanType = getApplicationContext().getType(beanName);
                } catch (Throwable ex) {
                    // 對於沒法解析的類型,會忽略
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                    }
                }
                // isHandler由不一樣策略的子類實現
                if (beanType != null && isHandler(beanType)) {
                    // 會探測知足類型的 Handler下的全部方法
                    detectHandlerMethods(beanName);
                }
            }
        }
        // 空實現:子類可擴展
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void detectHandlerMethods(final Object handler) {

        // 獲取 Handler類型
        Class<?> handlerType = (handler instanceof String ?
                getApplicationContext().getType((String) handler) : handler.getClass());
        // 獲取本來的類型,以防個別會被 Ciglib加強代理
        final Class<?> userType = ClassUtils.getUserClass(handlerType);

        // MethodIntrospector.selectMethods幾乎是對上面 
        // DefaultAnnotationHandlerMapping.determineUrlsForHandlerMethods的封裝
        // 這裏不作展開講解,大體作的就是把 @RequestMapping標識的方法找出來
        // key-就是符合要求的方法,value-@RequestMapping的屬性封裝的 RequestMappingInfo對象
        Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                new MethodIntrospector.MetadataLookup<T>() {
                    @Override
                    public T inspect(Method 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);
        }
        for (Map.Entry<Method, T> entry : methods.entrySet()) {
            // 挑選一個可調用的方法
            Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
            T mapping = entry.getValue();
            // 註冊 HandlerMethod
            registerHandlerMethod(handler, invocableMethod, mapping);
        }
    }

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        this.mappingRegistry.register(mapping, handler, method);
    }

    class MappingRegistry {

        // 存放 RequestMappingInfo和 MappingRegistration映射關係
        private final Map<T, MappingRegistration<T>> registry = new HashMap<T, MappingRegistration<T>>();

        // 存放 RequestMappingInfo和 HandlerMethod映射關係
        // 校驗以防止同一個 RequestMappingInfo對應多個 HandlerMethod
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<T, HandlerMethod>();
        
        // 存放直接
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();

        //
        private final Map<String, List<HandlerMethod>> nameLookup =
                new ConcurrentHashMap<String, List<HandlerMethod>>();

        // 存放 HandlerMethod和跨域註解 @CrossOrigin配置的映射關係
        private final Map<HandlerMethod, CorsConfiguration> corsLookup =
                new ConcurrentHashMap<HandlerMethod, CorsConfiguration>();

        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        public void register(T mapping, Object handler, Method method) {
            this.readWriteLock.writeLock().lock();
            try {
                // 使用 Handler和 Method封裝成 HandlerMethod
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);

                // mappingLookup的校驗,目的見註釋↑
                assertUniqueMethodMapping(handlerMethod, mapping);

                if (logger.isInfoEnabled()) {
                    logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
                }
                // 存儲 RequestMappingInfo和 handlerMethod映射關係
                this.mappingLookup.put(mapping, handlerMethod);

                // 篩選出不帶通配符(例如 "/*" or "/?")的 url放入 urlLookup
                // urlLookup爲 MultiValueMap類型,即 Map<String,List<T>>
                // key:@RequestMapping指定的不帶通配符的屬性"value",value:List<RequestMappingInfo>
                // 這種一對多的結構是預防,會有多個方法對應相同的請求路徑(好比 restful風格的接口)
                List<String> directUrls = getDirectUrls(mapping);
                for (String url : directUrls) {
                    this.urlLookup.add(url, mapping);
                }

                String name = null;
                // 默認使用 RequestMappingInfoHandlerMethodMappingNamingStrategy
                if (getNamingStrategy() != null) {
                    // 若是 mapping未指定 name,則使用 類名中的大寫字母組合#方法名
                    // 例如:UserController.test() ——> UC#test()
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    // 放入 nameLookup
                    addMappingName(name, handlerMethod);
                }

                // @CrossOrigin支持,用於支持請求跨域
                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) {
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                // 放入 registry
                this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
            } finally {
                this.readWriteLock.writeLock().unlock();
            }
        }
    }
}

    從該分支抽象類的命名也可以看出,這個分支將具體的處理器 handler 和 具體調用的方法 method 封裝成了 HandlerMethod 類型對象,不少映射關係都跟該對象有關。

    與另外一個分支不一樣的是,該分支不是經過重寫 initApplicationContext 來探測處理器的,它依舊沿用了父類該方法的邏輯去初始化「攔截器」,但探測處理器(Handler)則是利用了另外一個生命週期接口 InitializingBean.afterPropertiesSet。該方法在 bean 的初始化邏輯中被調用。(見 實例建立(下)invokeInitMethods 方法)

    接下來來看看 RequestMappingHandlerMapping 是如何實現 isHandler  getMappingForMethod,來判斷哪些才實例能做爲 Handler ,以及對 RequestMappingInfo 的封裝。

RequestMappingHandlerMapping

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

    @Override
    protected boolean isHandler(Class<?> beanType) {
        // 被 @Controller或 @RequestMapping標識的類
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

    /**
     * 若是方法被標識 @RequestMapping,則封裝成 RequestMappingInfo
     * 若是方法未被標識 @RequestMapping,則返回 null
     */
    @Override
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        // 用方法上的 @RequestMapping屬性值封裝 RequestMappingInfo
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            // 用類上的 @RequestMapping屬性值封裝 RequestMappingInfo
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                // 將類級別和方法級別的結合,返回新的 RequestMappingInfo
                // 這裏能夠看出類級別的適用於該類下全部的方法
                info = typeInfo.combine(info);
            }
        }
        return info;
    }

    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        RequestMapping requestMapping = 
             AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        // 若是方法未被標識 @RequestMapping,則返回 null
        return (requestMapping != null ? 
                createRequestMappingInfo(requestMapping, condition) : null);
    }

    protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, RequestCondition<?> customCondition) {
        // 用方法上的 @RequestMapping屬性值封裝 RequestMappingInfo
        return RequestMappingInfo
                .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
                .methods(requestMapping.method())
                .params(requestMapping.params())
                .headers(requestMapping.headers())
                .consumes(requestMapping.consumes())
                .produces(requestMapping.produces())
                .mappingName(requestMapping.name())
                .customCondition(customCondition)
                .options(this.config)
                .build();
    }
}

    RequestMappingHandlerMapping 做爲 DefaultAnnotationHandlerMapping (廢棄)的替代者,斷定標準依然是 @Controller 或 @RequestMapping,講到這裏,你們應該就明白咱們平時使用的這兩個註解到底是怎樣被框架所識別了。

 

總結

    經過對兩個分支的梳理,應該能清楚的看到 SpringMVC 初始化映射關係的時機,以及具體都作了哪些邏輯處理。正是這裏將請求的 url 和真正被調用的 Handler創建起了聯繫,以後的請求就能正確的找處處理器(Handler)。

相關文章
相關標籤/搜索