Spring Webflux --handler
提供包括抽象基類在內(nèi)的HandlerMapping實(shí)現(xiàn)。
先扔一張整體的diagram類圖:
AbstractHandlerMapping
HandlerMapping實(shí)現(xiàn)的抽象基類。
接口
- java.lang.Object
- org.springframework.context.support.ApplicationObjectSupport
- org.springframework.web.reactive.handler.AbstractHandlerMapping
- org.springframework.context.support.ApplicationObjectSupport
實(shí)現(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);
}
查找給定請(qǐng)求的handler仰迁,如果找不到特定的請(qǐng)求,則返回一個(gè)空的Mono。這個(gè)方法被getHandler(org.springframework.web.server.ServerWebExchange)調(diào)用硬猫。
在CORS 預(yù)先請(qǐng)求中,該方法應(yīng)該返回一個(gè)匹配改执,而不是預(yù)先請(qǐng)求的請(qǐng)求啸蜜,而是基于URL路徑的預(yù)期實(shí)際請(qǐng)求,從“Access-Control-Request-Method”頭辈挂,以及“Access-Control-Request-Headers”頭的HTTP方法衬横,通過(guò) getcorsconfiguration獲得CORS配置來(lái)允許通過(guò),
如果匹配到一個(gè)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;
}
抽象類實(shí)現(xiàn)的主要的具體方法,來(lái)獲得具體的Handle,實(shí)現(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實(shí)現(xiàn)的抽象基類噪叙。
接口
- 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.AbstractHandlerMapping
- org.springframework.context.support.ApplicationObjectSupport
支持直接匹配,例如注冊(cè)的“/ test”匹配“/ test”霉翔,以及各種ant樣式匹配睁蕾,例如, “/ test *”匹配“/ test”和“/ team”,“/ test / *”匹配“/ test”下的所有路徑子眶, 瀑凝。有關(guān)詳細(xì)信息,請(qǐng)參閱PathPattern javadoc臭杰。
將搜索所有路徑模式以查找當(dāng)前請(qǐng)求路徑的最具體匹配粤咪。最具體的模式定義為使用最少捕獲變量和通配符的最長(zhǎng)路徑模式。
private final Map<PathPattern, Object> handlerMap = new LinkedHashMap<>();
返回注冊(cè)路徑模式和handle的只讀視圖渴杆,這些注冊(cè)路徑模式和handle可能是一個(gè)實(shí)際的handle實(shí)例或延遲初始化handle的bean名稱寥枝。
public final Map<PathPattern, Object> getHandlerMap() {
return Collections.unmodifiableMap(this.handlerMap);
}
我們可以再看到下面這兩個(gè)方法實(shí)現(xiàn)了handle的注冊(cè),會(huì)把所有的路徑映射将塑,和handle實(shí)例放在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ù)處理映射的路徑脉顿,如果不以/
開(kāi)頭就加上/
private static String prependLeadingSlash(String pattern) {
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
return "/" + pattern;
}
else {
return pattern;
}
}
在這一步的時(shí)候開(kāi)始獲取內(nèi)部的handle蝌麸,查找給定請(qǐng)求的handle点寥,如果找不到特定的請(qǐng)求,則返回一個(gè)空的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的類敢辩,先獲取請(qǐng)求的url地址,調(diào)用lookupHandler(lookupPath, exchange)
去找這個(gè)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)
來(lái)匹配handle戚长,驗(yàn)證過(guò)后,然后設(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的實(shí)現(xiàn)同廉,來(lái)把url請(qǐng)求映射到對(duì)應(yīng)的request handler
的bean
支持映射到bean實(shí)例和映射到bean名稱;非單例的handler需要映射到bean名稱。
“urlMap”屬性適合用bean實(shí)例填充處理程序映射柑司∑刃ぃ可以通過(guò)java.util.Properties類接受的形式,通過(guò)“映射”屬性設(shè)置映射到bean名稱攒驰,如下所示:
/welcome.html=ticketController
/show.html=ticketController
語(yǔ)法是PATH = HANDLER_BEAN_NAME蟆湖。如果路徑不是以斜杠開(kāi)始的,則給它自動(dòng)補(bǔ)充一個(gè)斜杠玻粪。
支持直接匹配隅津,例如注冊(cè)的“/ test”匹配“/ test”,以及各種ant樣式匹配劲室,例如伦仍, “/ test *”匹配“/ test”和“/ team”,“/ test / *”匹配“/ test”下的所有路徑很洋, 充蓝。有關(guān)詳細(xì)信息,請(qǐng)參閱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
- org.springframework.web.reactive.handler.AbstractUrlHandlerMapping
- org.springframework.web.reactive.handler.AbstractHandlerMapping
- org.springframework.context.support.ApplicationObjectSupport
private final Map<String, Object> urlMap = new LinkedHashMap<>();
程序啟動(dòng)遍歷的時(shí)候把加載到的所有映射路徑棺克,和handle設(shè)置到urlMap
public void setUrlMap(Map<String, ?> urlMap) {
this.urlMap.putAll(urlMap);
}
獲得所有的urlMap悠垛,允許urlMap訪問(wèn)URL路徑映射,可以添加或覆蓋特定條目娜谊。
public Map<String, ?> getUrlMap() {
return this.urlMap;
}
初始化程序上下文确买,除了父類的初始化,還調(diào)用了registerHandler
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}
開(kāi)始注冊(cè)handler纱皆,注冊(cè)u(píng)rlMap中為相應(yīng)路徑指定的所有的handler湾趾。
如果handler不能注冊(cè),拋出 BeansException
如果有注冊(cè)的handler有沖突派草,比如兩個(gè)相同的搀缠,拋出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方法