天天看點

@FeignClient源碼淺析

Spring如何識别@FeignClient

從@EnableFeignClients 出發,尋找Spring如何識别FeignClient

從源碼中檢視到@Import(FeignClientsRegistrar.class)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
// 省略内部代碼
}           

檢視FeignClientsRegistrar 的類圖如下,

ResourceLoaderAware 注入 ResourceLoader

EnvironmentAware 注入 Environment

ImportBeanDefinitionRegistrar: 注冊額外的beanDefinition

@FeignClient源碼淺析

ImportBeanDefinitionRegistrar# registerBeanDefinitions ,FeignClientsRegistrar 的實作如下:

@Override
	public void registerBeanDefinitions( 
        AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 處理@EnableFeignClients上的defaultConfiguration配置
		registerDefaultConfiguration(metadata, registry);
        // 處理@FeignClient注解
		registerFeignClients(metadata, registry);
    }           

查閱registerFeignClients 部分的代碼,大緻邏輯為找到@FeignClient标注的接口,注冊到Spring,那注冊到Spring的Bean是什麼呢??

我們一起來檢視下registerFeignClient 方法, FeignClientFactoryBean 就是我們要找的主角了。

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 做了什麼?

@FeignClient源碼淺析

這裡我們關注FactoryBean#getObject,(其他擴充點從源碼中檢視并不重要)

getObject 委托給了getTarget(): 内部代碼,有2點關注,

一個就是說明了負載均衡的client是FeignBlockingLoadBalancerClient,

二,最終委托給了,org.springframework.cloud.openfeign.Targeter.target

Targeter 的預設實作是DefaultTargeter ,内部調用了Feign#target

Feign#target 的代碼如下:

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 是其内部實作 。

這段代碼的大緻邏輯如下:

@FeignClient源碼淺析

總結下就是:

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

之後我們來看ReflectiveFeign# newInstance()

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

@FeignClient源碼淺析

FeignClientFactoryBean#getObject 第二步 依托ReflectiveFeign #newInstance ,

使用JDK動态代理實作,對接口的增強,

ReflectiveFeign.FeignInvocationHandler 有調用的邏輯

ReflectiveFeign.FeignInvocationHandler 邏輯

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

代碼很長,這裡跳過,直接給出最終的 邏輯調用鍊條

@FeignClient源碼淺析

​編輯

FeignInvocationHandler 政策模式,委托給MethodHandler ,

SynchronousMethodHandler 底層依托于LoadBalanceClient 實作負載均衡

LoadBalanceClient 的實作是FeignBlockingLoadBalanceClient

LoadBalanceClient#choose 底層依托于ReactiveLoadBalancer#choose

ReactiveLoadBalancer 的一個實作是NacosLoaderBalance#choose

至此,FeignClient的大緻邏輯就分析完了。