Spring-cloud Feign 的深入理解
feign的调用流程
读取注解信息:enablefeignclients-->feignclientsregistrar-->feignclientfactorybean
feigh流程:reflectivefeign-->contract-->synchronousmethodhandler
相关configuration:feignclientsconfiguration,feignautoconfiguration,defaultfeignloadbalancedconfiguration,feignribbonclientautoconfiguration(ribbon)
在feignclientsregistrar中:
@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:
@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类型:
@bean @scope("prototype") @conditionalonmissingbean public feign.builder feignbuilder(retryer retryer) { return feign.builder().retryer(retryer); }
feign的builder.build返回了一个reflectivefeign:
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:
/** * 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方法:
@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
@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); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: InnoDB数据库死锁问题处理
下一篇: java代码执行字符串中的逻辑运算方法