SpringMVC之源碼分析--HandlerMapping(二)

概述

在前一章http://www.javashuo.com/article/p-mowcxnor-kr.html的基礎上繼續分析,主要完成SimpleUrlHandlerMapping類的原理。web

本系列文章是基於Spring5.0.5RELEASE。spring

類圖

在分析類以前,先了解下類的繼承關係,以下圖:segmentfault

紅框的類就是咱們本章要分析的類。app

建立/初始化

從類圖關係上能夠看出,SimpleUrlHanderMapping類最終實現了ApplicationContextAware接口,該接口定義了方法setApplicationContext(applicationContext),其做用是實現該接口的類,在Spring實例化類時,自動調用setApplicationContext(applicationContext)方法。ide

ApplicationObjectSupport抽象類實現了ApplicationContextAware接口的setApplicationContext(applicationContext)方法。學習

因爲WebApplicationObjectSupport抽象類重寫了父類ApplicationObjectSupport的initApplicationContext(context)方法,因此此時會調用WebApplicationObjectSupport的initAppliationContext(context)方法,在該方法中經過suppr調用父類的initApplicationContext(context)方法,該方法經過模板方法模式最終調到SimpleUrlHandlerMapping類的initApplicationContext()方法。this

整個建立涉及的類比較多,過程比較複雜,文字描述也很乏味,因此我畫了調用時序圖,可供你們參考:url

分析到此,咱們就找到了SimpleUrlHandlerMapping類的入口方法,即本類的initApplicationContext()方法。spa

分析

  • SimpleUrlHandlerMapping

該類間接實現了org.springframework.web.servlet.HandlerMapping接口,直接實現該接口的是org.springframework.web.servlet.handler.AbstractHandlerMapping抽象類,映射Url與請求handler bean。支持映射bean實例和映射bean名稱。源代碼以下:debug

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
    // 存儲url和bean映射
    private final Map<String, Object> urlMap = new LinkedHashMap<>();
    // 注入property的name爲mappings映射
    public void setMappings(Properties mappings) {
        CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
    }
    // 注入property的name爲urlMap映射
    public void setUrlMap(Map<String, ?> urlMap) {
        this.urlMap.putAll(urlMap);
    }
    public Map<String, ?> getUrlMap() {
        return this.urlMap;
    }
    // 實例化本類實例入口
    @Override
    public void initApplicationContext() throws BeansException {
        // 調用父類AbstractHandlerMapping的initApplicationContext方法,只要完成攔截器的註冊
        super.initApplicationContext();
        // 處理url和bean name,具體註冊調用父類完成
        registerHandlers(this.urlMap);
    }
    // 註冊映射關係,及將property中的值解析到map對象中,key爲url,value爲bean id或name
    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) -> {
                // 增長以"/"開頭
                if (!url.startsWith("/")) {
                    url = "/" + url;
                }
                // 去除handler bean名稱的空格
                if (handler instanceof String) {
                    handler = ((String) handler).trim();
                }
                // 調用父類AbstractUrlHandlerMapping完成映射
                registerHandler(url, handler);
            });
        }
    }

}

從以上代碼可知,SimpleUrlHandlerMapping類主要接收用戶設定的url與handler的映射關係,其實際的工做都是交由其父類來完成的。

  • AbstractHandlerMapping

在建立初始化SimpleUrlHandlerMapping類時,調用其父類的initApplicationContext()方法,該方法完成攔截器的初始化,代碼以下:

@Override
protected void initApplicationContext() throws BeansException {
    // 空實現。
    // 子類可重寫此方法以註冊額外的攔截器
    extendInterceptors(this.interceptors);
    // 從上下文中查詢攔截器並添加到攔截器列表中
    detectMappedInterceptors(this.adaptedInterceptors);
    // 初始化攔截器
    initInterceptors();
}

// 查找實現了MappedInterceptor接口的bean,並添加到映射攔截器列表
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
    mappedInterceptors.addAll(
            BeanFactoryUtils.beansOfTypeIncludingAncestors(
                    obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

// 將自定義bean設置到適配攔截器中,bean需實現HandlerInterceptor或WebRequestInterceptor
protected void initInterceptors() {
    if (!this.interceptors.isEmpty()) {
        for (int i = 0; i < this.interceptors.size(); i++) {
            Object interceptor = this.interceptors.get(i);
            if (interceptor == null) {
                throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
            }
            this.adaptedInterceptors.add(adaptInterceptor(interceptor));
        }
    }
}
  • AbstractUrlHandlerMapping

在建立初始化SimpleUrlHandlerMapping類時,調用AbstractUrlHandlerMapping類的registerHandler(urlPath,handler)方法,該方法源碼以下:

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;

    // 不是懶加載,默認爲false,即不是,經過配置SimpleUrlHandlerMapping屬性lazyInitHandlers的值進行控制
    // 若是不是懶加載而且handler爲單例,即從上下文中查詢實例處理,此時resolvedHandler爲handler實例對象;
    // 若是是懶加載或者handler不是單例,即resolvedHandler爲handler邏輯名
    if (!this.lazyInitHandlers && handler instanceof String) {
        String handlerName = (String) handler;
        ApplicationContext applicationContext = obtainApplicationContext();
        // 若是handler是單例,經過bean的scope控制
        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 {
            // 把url與handler(名稱或實例)放入map,以供後續使用
            this.handlerMap.put(urlPath, resolvedHandler);
            if (logger.isInfoEnabled()) {
                logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
            }
        }
    }
}

到此,SimpleUrlHandlerMapping類在容器啓動期間的初始化完成。

總結

本文分析了SimpleUrlHandlerMapping類初始化過程,其實核心就是把url和handler進行了映射,供後續訪問使用,單靠看文章沒法掌握。整個過程調用很複雜,你們多debug跟蹤,必定能瞭解其內部的邏輯。你們共勉!

最後建立了qq羣方便你們交流,可掃描加入,同時也可加我qq:276420284,共同窗習、共同進步,謝謝!

相關文章
相關標籤/搜索