Spring Flux中的核心DispatcherHandler的處理過程分爲三步,其中首步就是經過HandlerMapping接口查找Request所對應的Handler。本文就是經過閱讀源碼的方式,分析一下HandlerMapping接口的實現者之一——RequestMappingHandlerMapping類,用於處理基於註解的路由策略,把全部用@Controller和@RequestMapping標記的類中的Handler識別出來,以便DispatcherHandler調用的。java
HandlerMapping接口的另外兩種實現類:一、RouterFunctionMapping用於函數式端點的路由;二、SimpleUrlHandlerMapping用於顯式註冊的URL模式與WebHandler配對。
<!-- more -->react
文章系列web
Spring中基於註解的控制器的使用方法大體以下:spring
@Controller public class MyHandler{ @RequestMapping("/") public String handlerMethod(){ } }
在Spring WebFlux中,對上述使用方式進行了三層抽象模型。segmentfault
Mapping設計模式
Handler緩存
Method服務器
基於上述三層抽象模型,進而能夠做一些組合。併發
HandlerMethodapp
Mapping vs HandlerMethod
理解了這個抽象模型後,接下來分析源碼來理解Spring WebFlux如何處理請求與Handler之間的Mapping關係時,就很是容易了。
HandlerMapping接口及其各實現類負責上述模型的構建與運做。
HandlerMapping接口實現,採用了"模版方法"這種設計模式。
1層:AbstractHandlerMapping implements HandlerMapping, Ordered, BeanNameAware
^ |
2層:AbstractHandlerMethodMapping implements InitializingBean
^ |
3層:RequestMappingInfoHandlerMapping
^ |
4層:RequestMappingHandlerMapping implements EmbeddedValueResolverAware
下面對各層的職責做簡要說明:
第1層主要實現了對外提供模型的接口
第2層有兩個責任 —— 解析用戶定義的HandlerMethod + 實現對外提供模型接口實現所需的抽象方法
小結一下,就是HandlerMapping接口及其實現類,把用戶定義的各Controller等,抽象爲上述的Mapping、Handler及Method模型,並將Mapping與HandlerMethod做爲字典關係存起來,還提供經過匹配請求來得到HandlerMethod的公共方法。
接下來的章節,將先分析解析用戶定義的模型並緩存模型的過程,而後再分析一下匹配請求來得到HandlerMethod的公共方法的過程。
第2層AbstractHandlerMethodMapping抽象類中的一個重要方法——實現了InitializingBean接口的"void afterPropertiesSet()"方法,爲Spring WebFlux帶來了解析用戶定義的模型並緩存模型的機會 —— Spring容器初初始化完成該類的具體類的Bean後,將會回調這個方法。
在該方法中,實現獲取用戶定義的Handler、Method、Mapping以及緩存Mapping與HandlerMethod映射關係的功能。
@Override public void afterPropertiesSet() { initHandlerMethods(); // Total includes detected mappings + explicit registrations via registerMapping.. ... }
afterPropertiesSet方法中主要是調用了void initHandlerMethods()方法,具體以下:
protected void initHandlerMethods() { //獲取Spring容器中全部Bean名字 String[] beanNames = obtainApplicationContext().getBeanNamesForType(Object.class); for (String beanName : beanNames) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { Class<?> beanType = null; try { //獲取Bean的類型 beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } } //若是獲取到類型,而且類型是Handler,則繼續加載Handler方法。 if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } } } //初始化後收尾工做 handlerMethodsInitialized(getHandlerMethods()); }
這兒首先獲取Spring容器中全部Bean名字,而後循環處理每個Bean。若是Bean名稱不是以SCOPED_TARGET_NAME_PREFIX常量開頭,則獲取Bean的類型。若是獲取到類型,而且類型是Handler,則繼續加載Handler方法。
isHandler(beanType)調用,檢查Bean的類型是否符合handler定義。
AbstractHandlerMethodMapping抽象類中定義的抽象方法"boolean isHandler(Class<?> beanType)",是由RequestMappingHandlerMapping類實現的。具體實現代碼以下:
protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }
不難看出,對於RequestMappingHandlerMapping這個實現類來講,只有擁有@Controller或者@RequestMapping註解的類,纔是Handler。(言下之意對於其餘實現類來講Handler的定義不一樣)。
具體handler的定義,在HandlerMapping各實現類來講是不一樣的,這也是isHandler抽象方法由具體實現類來實現的緣由。
接下來咱們要重點看一下"detectHandlerMethods(beanName);"這個方法調用。
protected void detectHandlerMethods(final Object handler) { Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { //將handlerType轉換爲用戶類型(一般等同於被轉換的類型,不過諸如CGLIB生成的子類會被轉換爲原始類型) final Class<?> userType = ClassUtils.getUserClass(handlerType); //尋找目標類型userType中的Methods,selectMethods方法的第二個參數是lambda表達式,即感興趣的方法的過濾規則 Map<Method, T> methods = MethodIntrospector.selectMethods(userType, //回調函數metadatalookup將經過controller定義的mapping與手動定義的mapping合併起來 (MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType)); if (logger.isTraceEnabled()) { logger.trace("Mapped " + methods.size() + " handler method(s) for " + userType + ": " + methods); } methods.forEach((key, mapping) -> { //再次覈查方法與類型是否匹配 Method invocableMethod = AopUtils.selectInvocableMethod(key, userType); //若是是知足要求的方法,則註冊到全局的MappingRegistry實例裏 registerHandlerMethod(handler, invocableMethod, mapping); }); } }
首先將參數handler(即外部傳入的BeanName或者BeanType)轉換爲Class<?>類型變量handlerType。若是轉換成功,再將handlerType轉換爲用戶類型(一般等同於被轉換的類型,不過諸如CGLIB生成的子類會被轉換爲原始類型)。接下來獲取該用戶類型裏全部的方法(Method)。循環處理每一個方法,若是是知足要求的方法,則註冊到全局的MappingRegistry實例裏。
其中,如下代碼片斷有必要深刻探究一下
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> getMappingForMethod(method, userType));
MethodIntrospector.selectMethods方法的調用,將會把用@RequestMapping標記的方法篩選出來,並交給第二個參數所定義的MetadataLookup回調函數將經過controller定義的mapping與手動定義的mapping合併起來。
第二個參數是用lambda表達式傳入的,表達式中將method、userType傳給getMappingForMethod(method, userType)方法。
getMappingForMethod方法在高層次中是抽象方法,具體的是如今第4層RequestMappingHandlerMapping類中實現。在具體實現getMappingForMethod時,會調用到RequestMappingHandlerMapping類的下面這個方法。從該方法中,咱們能夠看到,首先會得到參數element(即用戶在Controller中定義的方法)的RequestMapping類型的類實例,而後構造表明Mapping抽象模型的RequestmappingInfo類型實例並返回。
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
構造表明Mapping抽象模型的RequestmappingInfo類型實例,用的是createRequestMappingInfo方法,以下。能夠看到RequestMappingInfo所須要的信息,包括paths、methods、params、headers、consumers、produces、mappingName,即用戶定義@RequestMapping註解時所設定的可能的參數,都被存在這兒了。擁有了這些信息,當請求來到時,RequestMappingInfo就能夠測試自身是不是處理該請求的人選之一了。
protected RequestMappingInfo createRequestMappingInfo( RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) { RequestMappingInfo.Builder builder = RequestMappingInfo .paths(resolveEmbeddedValuesInPatterns(requestMapping.path())) .methods(requestMapping.method()) .params(requestMapping.params()) .headers(requestMapping.headers()) .consumes(requestMapping.consumes()) .produces(requestMapping.produces()) .mappingName(requestMapping.name()); if (customCondition != null) { builder.customCondition(customCondition); } return builder.options(this.config).build(); }
最後,registerHandlerMethod(handler, invocableMethod, mapping)調用將緩存HandlerMethod,其中mapping參數是RequestMappingInfo類型的。。
內部調用的是MappingRegistry實例的void register(T mapping, Object handler, Method method)方法,其中T是RequestMappingInfo類型。
MappingRegistry類維護全部指向Handler Methods的映射,並暴露方法用於查找映射,同時提供併發控制。
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); ...... this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
AbstractHandlerMethodMapping類的「Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange)」方法,具體實現了根據請求查找HandlerMethod的邏輯。
@Override public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) { //獲取讀鎖 this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod; try { //調用其它方法繼續查找HandlerMethod handlerMethod = lookupHandlerMethod(exchange); } catch (Exception ex) { return Mono.error(ex); } if (handlerMethod != null) { handlerMethod = handlerMethod.createWithResolvedBean(); } return Mono.justOrEmpty(handlerMethod); } //釋放讀鎖 finally { this.mappingRegistry.releaseReadLock(); } }
handlerMethod = lookupHandlerMethod(exchange)調用,繼續查找HandlerMethod。咱們繼續看一下HandlerMethod lookupHandlerMethod(ServerWebExchange exchange)方法的定義。爲方便閱讀,我把註釋也寫在了代碼裏。
protected HandlerMethod lookupHandlerMethod(ServerWebExchange exchange) throws Exception { List<Match> matches = new ArrayList<>(); //查找全部知足請求的Mapping,並放入列表mathes addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, exchange); if (!matches.isEmpty()) { //獲取比較器comparator Comparator<Match> comparator = new MatchComparator(getMappingComparator(exchange)); //使用比較器將列表matches排序 matches.sort(comparator); //將排在第1位的做爲最佳匹配項 Match bestMatch = matches.get(0); if (matches.size() > 1) { //將排在第2位的做爲次佳匹配項 Match secondBestMatch = matches.get(1); } handleMatch(bestMatch.mapping, bestMatch.handlerMethod, exchange); return bestMatch.handlerMethod; } else { return handleNoMatch(this.mappingRegistry.getMappings().keySet(), exchange); } }
理解了Spring WebFlux在獲取映射關係方面的抽象設計模型後,就很容易讀懂代碼,進而更加理解框架的具體處理方式,在使用框架時作到「知己知彼」。
原文:http://www.yesdata.net/2018/11/27/spring-flux-request-mapping/