- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- SpringCloud Gateway加載斷言predicates與過(guò)濾器filters的
我們今天的主角是Gateway網(wǎng)關(guān),一聽(tīng)名字就知道它基本的任務(wù)就是去分發(fā)路由。根據不同的指定名稱(chēng)去請求各個(gè)服務(wù),下面是Gateway官方的解釋?zhuān)?/p>
,其他的博主就不多說(shuō)了,大家多去官網(wǎng)看看,只有官方的才是最正確的,回歸主題,我們的過(guò)濾器與斷言如何加載進(jìn)來(lái)的,并且是如何進(jìn)行對請求進(jìn)行過(guò)濾的。
大家如果對SpringBoot自動(dòng)加載的熟悉的話(huà),一定知道要看一個(gè)代碼的源碼,要找到META-INF下的spring.factories,具體為啥的博主就不多說(shuō)了,網(wǎng)上也有很多講解自動(dòng)加載的源碼分析,今天就講解Gateway,所有項目三板斧:加依賴(lài)、寫(xiě)注解、弄配置
依賴(lài):
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
注解:?jiǎn)?dòng)類(lèi)上需要添加@EnableDiscoveryClient,啟動(dòng)服務(wù)發(fā)現
配置:
spring: cloud: gateway: routes: - id: after-route #id必須要唯一 uri: lb://product-center predicates: - After=2030-12-16T15:53:22.999+08:00[Asia/Shanghai] filters: - PrefixPath=/product-api
大家看到這個(gè)配置的時(shí)候,為什么我們寫(xiě)After斷言與PrefixPath過(guò)濾器,gateway就會(huì )自動(dòng)識別呢,那我們有沒(méi)有那一個(gè)地方可以看到所有的自帶的屬性呢?當然有,而且我們本篇就主要講解為什么gateway會(huì )自動(dòng)識別,并且我們要自己實(shí)現并且添加自定義屬性。開(kāi)始源碼解析第一步,找到自動(dòng)加載的類(lèi)一探究竟;
看到這里的時(shí)候,第一步就成功了,剩下的就是找到org.springframework.cloud.gateway.config.GatewayAutoConfiguration這個(gè)關(guān)鍵類(lèi),我們主要看看里面的兩個(gè)類(lèi)
@Bean public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> GatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator) { return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties); } @Bean @Primary //TODO: property to disable composite? public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) { return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators))); }
這倆個(gè)類(lèi)配置,大家可能非常熟悉,大家上手一個(gè)新知識點(diǎn)的時(shí)候,肯定會(huì )找一些快速入門(mén)的文章看看,博主還是習慣直接找官方的quick start來(lái)看,大家可以看看這些快速上手項目:
所以博主直接就找到了RouteLocator這個(gè)類(lèi)配置,果不其然,我們找到了斷言與過(guò)濾器的注入,雖然實(shí)在方法體內作為參數傳入,但是會(huì )被spring解析到,直接去工廠(chǎng)里拿到,具體怎么拿呢?我們再來(lái)看看:
public BeanWrapper instantiateUsingFactoryMethod( String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) { ..... for (Method candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); if (paramTypes.length >= minNrOfArgs) { ArgumentsHolder argsHolder; if (explicitArgs != null) { // Explicit arguments given -> arguments length must match exactly. if (paramTypes.length != explicitArgs.length) { continue; } argsHolder = new ArgumentsHolder(explicitArgs); } else { // Resolved constructor arguments: type conversion and/or autowiring necessary. try { String[] paramNames = null; ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { paramNames = pnd.getParameterNames(candidate); } //主要就是會(huì )進(jìn)入到這里去解析每一個(gè)參數類(lèi)型 argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring, candidates.length == 1); } catch (UnsatisfiedDependencyException ex) { if (logger.isTraceEnabled()) { logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex); } // Swallow and try next overloaded factory method. if (causes == null) { causes = new LinkedList<>(); } causes.add(ex); continue; } } int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this factory method if it represents the closest match. if (typeDiffWeight < minTypeDiffWeight) { factoryMethodToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousFactoryMethods = null; } // Find out about ambiguity: In case of the same type difference weight // for methods with the same number of parameters, collect such candidates // and eventually raise an ambiguity exception. // However, only perform that check in non-lenient constructor resolution mode, // and explicitly ignore overridden methods (with the same parameter signature). else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight && !mbd.isLenientConstructorResolution() && paramTypes.length == factoryMethodToUse.getParameterCount() && !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) { if (ambiguousFactoryMethods == null) { ambiguousFactoryMethods = new LinkedHashSet<>(); ambiguousFactoryMethods.add(factoryMethodToUse); } ambiguousFactoryMethods.add(candidate); } } } ..... return bw; }
每一個(gè)參數都需要解析,但是看這里不像沒(méi)關(guān)系,繼續往下走:就會(huì )看到
private ArgumentsHolder createArgumentArray( String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues, BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable, boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException { .... //這下就是了,每個(gè)參數都被進(jìn)行解析 for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { .... try { //我們的參數就是在這里被進(jìn)行解析的--resolveAutowiredArgument Object autowiredArgument = resolveAutowiredArgument( methodParam, beanName, autowiredBeanNames, converter, fallback); args.rawArguments[paramIndex] = autowiredArgument; args.arguments[paramIndex] = autowiredArgument; args.preparedArguments[paramIndex] = new AutowiredArgumentMarker(); args.resolveNecessary = true; } catch (BeansException ex) { throw new UnsatisfiedDependencyException( mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex); } } } //其他不重要的,直接忽略掉 ... return args; }
開(kāi)始解析的時(shí)看到了,我們需要把斷言和過(guò)濾器列表都加在進(jìn)來(lái),那spring是如何加載的呢?是根據方法體內傳入的類(lèi)型找到所有實(shí)現了斷言和過(guò)濾器工廠(chǎng)接口的類(lèi)并且進(jìn)行獲取實(shí)例,我們仔細這些工廠(chǎng)的實(shí)現類(lèi),就會(huì )找到我們的使用的一些屬性,比如我們例子中的PrefixPath過(guò)濾器和Path斷言;
protected Map<String, Object> findAutowireCandidates( @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { //主要的就是這個(gè),beanNamesForTypeIncludingAncestors方法,該方法就是從bean工廠(chǎng)中獲取所有當前類(lèi)的實(shí)現實(shí)例名稱(chēng), String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map<String, Object> result = new LinkedHashMap<>(candidateNames.length); ... //遍歷名稱(chēng),進(jìn)行實(shí)例化 for (String candidate : candidateNames) { if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } ..... return result; }
這下我們知道了,系統配置的斷言和過(guò)濾器是如何被加載 的了,那我們還有一個(gè)問(wèn)題,如果我自定義一個(gè),如何被系統識別呢?并且怎么進(jìn)行配置呢?不難發(fā)現我們之前看源碼時(shí),他是被spring通過(guò)找工廠(chǎng)實(shí)現類(lèi)找到并且加載進(jìn)來(lái)的,那我們自己實(shí)現工廠(chǎng)接口并且使用@Component注解,讓spring加載進(jìn)來(lái)不就的了嗎?但是你會(huì )發(fā)現系統自定義的屬性斷言或者過(guò)濾器都有工廠(chǎng)名字的后綴,這是為什么呢?影響我們自定義 的類(lèi)被加載到gateway中且生效嗎?事實(shí)是會(huì )影響,那為什么影響呢?我們還是看源碼。因為我們之前的類(lèi)加載還沒(méi)有看完,我們最開(kāi)始的時(shí)候就找到了兩個(gè)@bean 的自動(dòng)加載,那這兩個(gè)類(lèi)實(shí)例化的時(shí)候都做了哪些工作,我們還沒(méi)有細看;
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List<RoutePredicateFactory> predicates, List<GatewayFilterFactory> gatewayFilterFactories, GatewayProperties gatewayProperties) { this.routeDefinitionLocator = routeDefinitionLocator; initFactories(predicates); gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory)); this.gatewayProperties = gatewayProperties; }
initFactories(predicates):這段代碼主要是進(jìn)行解析斷言工廠(chǎng)實(shí)現類(lèi);并且放入一個(gè)Map中,
gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory)):跟斷言的代碼幾乎一樣,因為沒(méi)有其他多余的邏輯,所以沒(méi)有封裝到方法中,直接使用java8 的流特性,寫(xiě)完了遍歷的過(guò)程。大家要注意一段代碼就是factory.name(),這里使用了一個(gè)方法;
default String name() { return NameUtils.normalizeRoutePredicateName(getClass()); }
主要就是把當前類(lèi)包含工廠(chǎng)名字的部分去掉了,然后用剩下的字符串當key值,所以我們可以使用工廠(chǎng)名字做后墜,也可以不用,但是剩下的字符則是你要寫(xiě)進(jìn)配置的關(guān)鍵字,不過(guò)博主基本都是按照系統自帶屬性一樣,用的是工廠(chǎng)接口的名字做的后綴。
好了,今天就講解這么多,下次在講解gateway接到請求后,是如何進(jìn)行一步一步過(guò)濾的,何時(shí)進(jìn)行斷言校驗的。一次不講這么多,消化了就好。
到此這篇關(guān)于SpringCloud Gateway加載斷言predicates與過(guò)濾器filters的源碼分析的文章就介紹到這了,更多相關(guān)SpringCloud Gateway斷言?xún)热菡埶阉髂_本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站