@FeignClient源码浅析

2023-10-16 15:17:31 浏览数 (1)

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的大致逻辑就分析完了。 

0 人点赞