feign的調用流程
讀取注解信息:enablefeignclients-->feignclientsregistrar-->feignclientfactorybean
feigh流程:reflectivefeign-->contract-->synchronousmethodhandler
相關configuration:feignclientsconfiguration,feignautoconfiguration,defaultfeignloadbalancedconfiguration,feignribbonclientautoconfiguration(ribbon)
在feignclientsregistrar中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@override public void registerbeandefinitions(annotationmetadata metadata, beandefinitionregistry registry) { //注冊feign配置信息 registerdefaultconfiguration(metadata, registry); //注冊feign client registerfeignclients(metadata, registry); } private void registerfeignclient(beandefinitionregistry registry, annotationmetadata annotationmetadata, map<string, object> attributes) { string classname = annotationmetadata.getclassname(); //準備注入feignclientfactorybean beandefinitionbuilder definition = beandefinitionbuilder .genericbeandefinition(feignclientfactorybean. class ); ... } |
查看feignclientfactorybean:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
@override public object getobject() throws exception { feigncontext context = applicationcontext.getbean(feigncontext. class ); //構建feign.builder feign.builder builder = feign(context); //如果注解沒有指定url if (!stringutils.hastext( this .url)) { string url; if (! this .name.startswith( "http" )) { url = "http://" + this .name; } else { url = this .name; } url += cleanpath(); return loadbalance(builder, context, new hardcodedtarget<>( this .type, this .name, url)); } //如果指定了url if (stringutils.hastext( this .url) && ! this .url.startswith( "http" )) { this .url = "http://" + this .url; } string url = this .url + cleanpath(); client client = getoptional(context, client. class ); if (client != null ) { if (client instanceof loadbalancerfeignclient) { // 因為指定了url且classpath下有ribbon,獲取client的delegate(unwrap) // not load balancing because we have a url, // but ribbon is on the classpath, so unwrap client = ((loadbalancerfeignclient)client).getdelegate(); } builder.client(client); } targeter targeter = get(context, targeter. class ); return targeter.target( this , builder, context, new hardcodedtarget<>( this .type, this .name, url)); } protected <t> t loadbalance(feign.builder builder, feigncontext context, hardcodedtarget<t> target) { //獲取feign client實例 client client = getoptional(context, client. class ); if (client != null ) { builder.client(client); //defaulttargeter或者hystrixtargeter targeter targeter = get(context, targeter. class ); //調用builder的target,其中就調用了feign的newinstance return targeter.target( this , builder, context, target); } throw new illegalstateexception( "no feign client for loadbalancing defined. did you forget to include spring-cloud-starter-netflix-ribbon?" ); } |
在feignclientsconfiguration配置了feign.builder,prototype類型:
1
2
3
4
5
6
|
@bean @scope ( "prototype" ) @conditionalonmissingbean public feign.builder feignbuilder(retryer retryer) { return feign.builder().retryer(retryer); } |
feign的builder.build返回了一個reflectivefeign:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public feign build() { synchronousmethodhandler.factory synchronousmethodhandlerfactory = new synchronousmethodhandler.factory(client, retryer, requestinterceptors, logger, loglevel, decode404); parsehandlersbyname handlersbyname = new parsehandlersbyname(contract, options, encoder, decoder, errordecoder, synchronousmethodhandlerfactory); //reflectivefeign構造參數 //parsehandlersbyname作用是通過傳入的target返回代理接口下的方法的各種信息(methodhandler) //contract:解析接口的方法注解規則,生成methodmetadata //options:request超時配置 //encoder:請求編碼器 //decoder:返回解碼器 //errordecoder:錯誤解碼器 //synchronousmethodhandler.factory是構建synchronousmethodhandler的工廠 //client:代表真正執行http的組件 //retryer:該組決定了在http請求失敗時是否需要重試 //requestinterceptor:請求前的攔截器 //logger:記錄日志組件,包含各個階段記錄日志的方法和留給用戶自己實現的log方法 //logger.level:日志級別 //decode404:處理404的策略,返回空還是報錯 //synchronousmethodhandlerfactory通過所有的信息去包裝一個synchronousmethodhandler,在調用invoke方法的時候執行http return new reflectivefeign(handlersbyname, invocationhandlerfactory); } |
在調用feign.builder的target的時候,調用了reflectivefeign.newinstance:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/** * creates an api binding to the {@code target}. as this invokes reflection, care should be taken * to cache the result. */ @suppresswarnings ( "unchecked" ) @override //接收target參數(包含feign代理接口的類型class,名稱,http url) public <t> t newinstance(target<t> target) { //首先通過**parsehandlersbyname**解析出接口中包含的方法,包裝requesttemplate,組裝成<name, methodhandler> map<string, methodhandler> nametohandler = targettohandlersbyname.apply(target); map<method, methodhandler> methodtohandler = new linkedhashmap<method, methodhandler>(); //接口default方法list list<defaultmethodhandler> defaultmethodhandlers = new linkedlist<defaultmethodhandler>(); for (method method : target.type().getmethods()) { if (method.getdeclaringclass() == object. class ) { continue ; } else if (util.isdefault(method)) { defaultmethodhandler handler = new defaultmethodhandler(method); defaultmethodhandlers.add(handler); methodtohandler.put(method, handler); } else { methodtohandler.put(method, nametohandler.get(feign.configkey(target.type(), method))); } } //invocationhandlerfactory.default()返回了一個reflectivefeign.feigninvocationhandler對象,通過傳入的methodhandler map 調用目標對象的對應方法 invocationhandler handler = factory.create(target, methodtohandler); //生成jdk代理對象 t proxy = (t) proxy.newproxyinstance(target.type().getclassloader(), new class <?>[]{target.type()}, handler); //綁定接口的默認方法到代理對象 for (defaultmethodhandler defaultmethodhandler : defaultmethodhandlers) { defaultmethodhandler.bindto(proxy); } return proxy; } |
生成feign代理對象的基本流程圖:
當調用接口方法時,實際上就是調用代理對象invoke方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@override public object invoke(object[] argv) throws throwable { //工廠創建請求模版 requesttemplate template = buildtemplatefromargs.create(argv); //每次克隆一個新的retryer retryer retryer = this .retryer.clone(); while ( true ) { try { //這里調用實際的feign client execute return executeanddecode(template); } catch (retryableexception e) { //失敗重試 retryer.continueorpropagate(e); if (loglevel != logger.level.none) { logger.logretry(metadata.configkey(), loglevel); } continue ; } } } |
在defaultfeignloadbalancedconfiguration里實例化了loadbalancerfeignclient
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@override public response execute(request request, request.options options) throws ioexception { try { uri asuri = uri.create(request.url()); string clientname = asuri.gethost(); uri uriwithouthost = cleanurl(request.url(), clientname); //delegate這里是client.default實例,底層調用的是java.net原生網絡訪問 feignloadbalancer.ribbonrequest ribbonrequest = new feignloadbalancer.ribbonrequest( this .delegate, request, uriwithouthost); iclientconfig requestconfig = getclientconfig(options, clientname); //executewithloadbalancer會根據ribbon的負載均衡算法構建url,這里不展開 return lbclient(clientname).executewithloadbalancer(ribbonrequest, requestconfig).toresponse(); } catch (clientexception e) { ioexception io = findioexception(e); if (io != null ) { throw io; } throw new runtimeexception(e); } } |
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://segmentfault.com/a/1190000018077675