千家信息网

Spring-cloud Feign的示例分析

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,这篇文章给大家分享的是有关Spring-cloud Feign的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。feign的调用流程读取注解信息:EnableFeig
千家信息网最后更新 2025年01月18日Spring-cloud Feign的示例分析

这篇文章给大家分享的是有关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 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 loadBalance(Feign.Builder builder, FeignContext context,      HardCodedTarget 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 newInstance(Target target) {  //首先通过**ParseHandlersByName**解析出接口中包含的方法,包装RequestTemplate,组装成  Map nameToHandler = targetToHandlersByName.apply(target);  Map methodToHandler = new LinkedHashMap();  //接口default方法List  List defaultMethodHandlers = new LinkedList();  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的示例分析"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

0