内容简介:feign的调用流程读取注解信息:feigh流程:
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);
}
}
以上所述就是小编给大家介绍的《Spring-cloud Feign 的理解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 理解原型其实是理解原型链
- 要理解深度学习,必须突破常规视角去理解优化
- 深入理解java虚拟机(1) -- 理解HotSpot内存区域
- 荐 【C++100问】深入理解理解顶层const和底层const
- 深入理解 HTTPS
- 深入理解 HTTPS
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
从零开始做产品经理
萧七公子 / 中国华侨出版社 / 2016-12-1 / 27.9
《从零开始做产品经理:产品经理的第一本书》根据产品经理的能力需求与成长体系,共分为八章内容,从了解产品开始,到挖掘用户需求、进行产品设计、管理团队、进行项目管理、产品运营、把握产品的生命周期,以及产品经理的成长路径,全面阐释了产品经理的修炼之道。《从零开始做产品经理:产品经理的第一本书》书中信息量大,图文并茂,论点与论据相得益彰,并且融合了丰富的案例与故事,使得整个阅读过程妙趣横生而且迅速开“悟道......一起来看看 《从零开始做产品经理》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
XML 在线格式化
在线 XML 格式化压缩工具