Spring Webflux --源碼閱讀之 handler包

Spring Webflux --handler

提供包括抽象基類在內(nèi)的HandlerMapping實現(xiàn)。

先扔一張整體的diagram類圖:

simoleUrlHandlerMapping.jpg

AbstractHandlerMapping

HandlerMapping實現(xiàn)的抽象基類。

接口

  • java.lang.Object
    • org.springframework.context.support.ApplicationObjectSupport
      • org.springframework.web.reactive.handler.AbstractHandlerMapping

實現(xiàn)了HandlerMapping, Ordered

    public abstract class AbstractHandlerMapping extends
    ApplicationObjectSupport implements HandlerMapping, Ordered {



private static final WebHandler REQUEST_HANDLED_HANDLER = exchange -> Mono.empty();


private int order = Integer.MAX_VALUE;  // default: same as non-Ordered

private final PathPatternParser patternParser;

private final UrlBasedCorsConfigurationSource globalCorsConfigSource;

private CorsProcessor corsProcessor = new DefaultCorsProcessor();


public AbstractHandlerMapping() {
      this.patternParser = new PathPatternParser();
      this.globalCorsConfigSource = new UrlBasedCorsConfigurationSource(this.patternParser);
}

查找給定請求的handler,如果找不到特定的請求,則返回一個空的Mono。這個方法被getHandler(org.springframework.web.server.ServerWebExchange)調(diào)用。

在CORS 預(yù)先請求中,該方法應(yīng)該返回一個匹配,而不是預(yù)先請求的請求,而是基于URL路徑的預(yù)期實際請求,從“Access-Control-Request-Method”頭,以及“Access-Control-Request-Headers”頭的HTTP方法,通過 getcorsconfiguration獲得CORS配置來允許通過,

如果匹配到一個handler,就返回Mono

protected abstract Mono<?> getHandlerInternal(ServerWebExchange exchange);

檢索給定handle的CORS配置。

@Nullable
protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
    if (handler instanceof CorsConfigurationSource) {
        return ((CorsConfigurationSource) handler).getCorsConfiguration(exchange);
    }
    return null;
}

抽象類實現(xiàn)的主要的具體方法,來獲得具體的Handle,實現(xiàn)了HandlerMapping中的getHandler,Mono<Object> getHandler(ServerWebExchange exchange);

@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
    return getHandlerInternal(exchange).map(handler -> {
        if (CorsUtils.isCorsRequest(exchange.getRequest())) {
            CorsConfiguration configA = this.globalCorsConfigSource.getCorsConfiguration(exchange);
            CorsConfiguration configB = getCorsConfiguration(handler, exchange);
            CorsConfiguration config = (configA != null ? configA.combine(configB) : configB);
            if (!getCorsProcessor().process(config, exchange) ||
                    CorsUtils.isPreFlightRequest(exchange.getRequest())) {
                return REQUEST_HANDLED_HANDLER;
            }
        }
        return handler;
    });
}

AbstractUrlHandlerMapping

基于URL映射的HandlerMapping實現(xiàn)的抽象基類。

接口

  • java.lang.Object
    • org.springframework.context.support.ApplicationObjectSupport
      • org.springframework.web.reactive.handler.AbstractHandlerMapping
        • org.springframework.web.reactive.handler.AbstractUrlHandlerMapping

支持直接匹配,例如注冊的“/ test”匹配“/ test”,以及各種ant樣式匹配,例如, “/ test *”匹配“/ test”和“/ team”,“/ test / *”匹配“/ test”下的所有路徑, 。有關(guān)詳細信息,請參閱PathPattern javadoc。

將搜索所有路徑模式以查找當前請求路徑的最具體匹配。最具體的模式定義為使用最少捕獲變量和通配符的最長路徑模式。

private final Map<PathPattern, Object> handlerMap = new LinkedHashMap<>();

返回注冊路徑模式和handle的只讀視圖,這些注冊路徑模式和handle可能是一個實際的handle實例或延遲初始化handle的bean名稱。

public final Map<PathPattern, Object> getHandlerMap() {
    return Collections.unmodifiableMap(this.handlerMap);
}

我們可以再看到下面這兩個方法實現(xiàn)了handle的注冊,會把所有的路徑映射,和handle實例放在handlerMap中

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;

    // Parse path pattern
    urlPath = prependLeadingSlash(urlPath);
    PathPattern pattern = getPathPatternParser().parse(urlPath);
    if (this.handlerMap.containsKey(pattern)) {
        Object existingHandler = this.handlerMap.get(pattern);
        if (existingHandler != null) {
            if (existingHandler != resolvedHandler) {
                throw new IllegalStateException(
                        "Cannot map " + getHandlerDescription(handler) + " to [" + urlPath + "]: " +
                        "there is already " + getHandlerDescription(existingHandler) + " mapped.");
            }
        }
    }

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

    // Register resolved handler
    this.handlerMap.put(pattern, resolvedHandler);
    if (logger.isInfoEnabled()) {
        logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
    }
}

預(yù)處理映射的路徑,如果不以/開頭就加上/

private static String prependLeadingSlash(String pattern) {
    if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
        return "/" + pattern;
    }
    else {
        return pattern;
    }
}

在這一步的時候開始獲取內(nèi)部的handle,查找給定請求的handle,如果找不到特定的請求,則返回一個空的Mono。

    @Override
public Mono<Object> getHandlerInternal(ServerWebExchange exchange) {
    PathContainer lookupPath = exchange.getRequest().getPath().pathWithinApplication();
    Object handler;
    try {
        handler = lookupHandler(lookupPath, exchange);
    }
    catch (Exception ex) {
        return Mono.error(ex);
    }

    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 Mono.justOrEmpty(handler);
}

獲取到handle的類,先獲取請求的url地址,調(diào)用lookupHandler(lookupPath, exchange)去找這個handle。

@Nullable
protected Object lookupHandler(PathContainer lookupPath, ServerWebExchange exchange)
        throws Exception {

    return this.handlerMap.entrySet().stream()
            .filter(entry -> entry.getKey().matches(lookupPath))
            .sorted((entry1, entry2) ->
                    PathPattern.SPECIFICITY_COMPARATOR.compare(entry1.getKey(), entry2.getKey()))
            .findFirst()
            .map(entry -> {
                PathPattern pattern = entry.getKey();
                if (logger.isDebugEnabled()) {
                    logger.debug("Matching pattern for request [" + lookupPath + "] is " + pattern);
                }
                PathContainer pathWithinMapping = pattern.extractPathWithinPattern(lookupPath);
                return handleMatch(entry.getValue(), pattern, pathWithinMapping, exchange);
            })
            .orElse(null);
}

在這里又調(diào)用了handleMatch(entry.getValue(), pattern, pathWithinMapping, exchange)來匹配handle,驗證過后,然后設(shè)置到ServerWebExchange中最后返回。

private Object handleMatch(Object handler, PathPattern bestMatch, PathContainer pathWithinMapping,
        ServerWebExchange exchange) {

    // Bean name or resolved handler?
    if (handler instanceof String) {
        String handlerName = (String) handler;
        handler = obtainApplicationContext().getBean(handlerName);
    }

    validateHandler(handler, exchange);

    exchange.getAttributes().put(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
    exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestMatch);
    exchange.getAttributes().put(PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, pathWithinMapping);

    return handler;
}

SimpleUrlHandlerMapping

HandlerMapping的實現(xiàn),來把url請求映射到對應(yīng)的request handler的bean

支持映射到bean實例和映射到bean名稱;非單例的handler需要映射到bean名稱。

“urlMap”屬性適合用bean實例填充處理程序映射。可以通過java.util.Properties類接受的形式,通過“映射”屬性設(shè)置映射到bean名稱,如下所示:

/welcome.html=ticketController

/show.html=ticketController

語法是PATH = HANDLER_BEAN_NAME。如果路徑不是以斜杠開始的,則給它自動補充一個斜杠。

支持直接匹配,例如注冊的“/ test”匹配“/ test”,以及各種ant樣式匹配,例如, “/ test *”匹配“/ test”和“/ team”,“/ test / *”匹配“/ test”下的所有路徑, 。有關(guān)詳細信息,請參閱PathPattern javadoc。

接口

  • java.lang.Object
    • org.springframework.context.support.ApplicationObjectSupport
      • org.springframework.web.reactive.handler.AbstractHandlerMapping
        • org.springframework.web.reactive.handler.AbstractUrlHandlerMapping
          • org.springframework.web.reactive.handler.SimpleUrlHandlerMapping
private final Map<String, Object> urlMap = new LinkedHashMap<>();


程序啟動遍歷的時候把加載到的所有映射路徑,和handle設(shè)置到urlMap
public void setUrlMap(Map<String, ?> urlMap) {
    this.urlMap.putAll(urlMap);
}

獲得所有的urlMap,允許urlMap訪問URL路徑映射,可以添加或覆蓋特定條目。
public Map<String, ?> getUrlMap() {
    return this.urlMap;
}


初始化程序上下文,除了父類的初始化,還調(diào)用了registerHandler
@Override
public void initApplicationContext() throws BeansException {
    super.initApplicationContext();
    registerHandlers(this.urlMap);
}

開始注冊handler,注冊urlMap中為相應(yīng)路徑指定的所有的handler。
如果handler不能注冊,拋出 BeansException
如果有注冊的handler有沖突,比如兩個相同的,拋出java.lang.IllegalStateException

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
    if (urlMap.isEmpty()) {
        logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
    }
    else {
        for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
            String url = entry.getKey();
            Object handler = entry.getValue();
            // 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);
        }
    }
}

這里調(diào)用的registerHandler(url, handler)就是剛剛抽象類AbstractUrlHandlerMapping中的registerHandler方法

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

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

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