前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >@FeignClient源码浅析

@FeignClient源码浅析

作者头像
温安适
发布2023-10-16 15:17:31
1920
发布2023-10-16 15:17:31
举报
文章被收录于专栏:温安适的blog温安适的blog

Spring如何识别@FeignClient

从@EnableFeignClients 出发,寻找Spring如何识别FeignClient

从源码中查看到@Import(FeignClientsRegistrar.class)

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
// 省略内部代码
}

查看FeignClientsRegistrar 的类图如下,

ResourceLoaderAware 注入 ResourceLoader

EnvironmentAware 注入 Environment

ImportBeanDefinitionRegistrar: 注册额外的beanDefinition

ImportBeanDefinitionRegistrar# registerBeanDefinitions ,FeignClientsRegistrar 的实现如下:

代码语言:javascript
复制
    @Override
	public void registerBeanDefinitions( 
        AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 处理@EnableFeignClients上的defaultConfiguration配置
		registerDefaultConfiguration(metadata, registry);
        // 处理@FeignClient注解
		registerFeignClients(metadata, registry);
    }

查阅registerFeignClients 部分的代码,大致逻辑为找到@FeignClient标注的接口,注册到Spring,那注册到Spring的Bean是什么呢??

我们一起来查看下registerFeignClient 方法, FeignClientFactoryBean 就是我们要找的主角了。

代码语言:javascript
复制
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
			Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		Class clazz = ClassUtils.resolveClassName(className, null);
		ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
				? (ConfigurableBeanFactory) registry : null;
		String contextId = getContextId(beanFactory, attributes);
		String name = getName(attributes);
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
    // 省略大部分代码
	}

至此我们可以得出如下结论:

FeignClientsRegistrar 实现了ImportBeanDefinitionRegistrar# registerBeanDefinitions 方法,内部扫描@FeignClient注解的接口,转化为 FeignClientFactoryBean  注入Spring。

FeignClientFactoryBean  做了什么?

 这里我们关注FactoryBean#getObject,(其他扩展点从源码中查看并不重要)

getObject 委托给了getTarget(): 内部代码,有2点关注,

一个就是说明了负载均衡的client是FeignBlockingLoadBalancerClient,

二,最终委托给了,org.springframework.cloud.openfeign.Targeter.target 

Targeter 的默认实现是DefaultTargeter ,内部调用了Feign#target

Feign#target 的代码如下:

代码语言:javascript
复制
    public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }

    public Feign build() {
      Client client = Capability.enrich(this.client, capabilities);
      Retryer retryer = Capability.enrich(this.retryer, capabilities);
      List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
          .map(ri -> Capability.enrich(ri, capabilities))
          .collect(Collectors.toList());
      Logger logger = Capability.enrich(this.logger, capabilities);
      Contract contract = Capability.enrich(this.contract, capabilities);
      Options options = Capability.enrich(this.options, capabilities);
      Encoder encoder = Capability.enrich(this.encoder, capabilities);
      Decoder decoder = Capability.enrich(this.decoder, capabilities);
      InvocationHandlerFactory invocationHandlerFactory =
          Capability.enrich(this.invocationHandlerFactory, capabilities);
      QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);

      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
  }

ReflectiveFeign 是其内部实现 。

这段代码的大致逻辑如下:

 总结下就是:

FeignClientFactoryBean#getObject  第一步 Feign.Builder#build() 创建ReflectiveFeign

之后我们来看ReflectiveFeign# newInstance()

代码语言:javascript
复制
  public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    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)));
      }
    }
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }

内部是JDK的动态代理,核心逻辑在ReflectiveFeign.FeignInvocationHandler

FeignClientFactoryBean#getObject  第二步 依托ReflectiveFeign #newInstance ,使用JDK动态代理实现,对接口的增强,ReflectiveFeign.FeignInvocationHandler 有调用的逻辑

ReflectiveFeign.FeignInvocationHandler 逻辑

代码语言:javascript
复制
static class FeignInvocationHandler implements InvocationHandler {

    private final Target target;
    private final Map<Method, MethodHandler> dispatch;

    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }

      return dispatch.get(method).invoke(args);
    }

策略模式,具体的执行委托给了MethodHandler ,MethodHandler的一个实现是SynchronousMethodHandler

代码很长,这里跳过,直接给出最终的 逻辑调用链条

FeignInvocationHandler  策略模式,委托给MethodHandler SynchronousMethodHandler 底层依托于LoadBalanceClient 实现负载均衡  LoadBalanceClient 的实现是FeignBlockingLoadBalanceClient  LoadBalanceClient#choose  底层依托于ReactiveLoadBalancer#choose  ReactiveLoadBalancer 的一个实现是NacosLoaderBalance#choose

至此,FeignClient的大致逻辑就分析完了。 

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-10-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring如何识别@FeignClient
  • FeignClientFactoryBean  做了什么?
  • ReflectiveFeign.FeignInvocationHandler 逻辑
相关产品与服务
负载均衡
负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档