图文Debug一步一步带你看清SpringCloud OpenFeign源码执行过程

611 阅读7分钟

这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党

版本

Version: spring-cloud-openfeign-core:3.0.1

Spring Cloud openfeign 使用

我们首先看看Spring Cloud openfeign 是如何使用的,然后基于使用进行一个源码分析

  1. 添加依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
<!--            <version>3.0.1</version>-->
        </dependency>
  1. 开启Feign
@SpringBootApplication
@EnableFeignClients
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
  1. 接口调用
@FeignClient(name = "test", url = "127.0.0.1:10080")
public interface PlutusClient {

    @RequestMapping(method = RequestMethod.GET, value ="/test/v1/testRpc")
    String testRpc();
}

源码分析

1. 开启Feign自动装配注解 @EnableFeignClients

  • @EnableFeignClients.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
  
  //等价于basePackages属性,更简洁的方式
  String[] value() default {};
  //指定多个包名进行扫描
  String[] basePackages() default {};
  //指定多个类或接口的class,扫描时会在这些指定的类和接口所属的包进行扫描
  Class<?>[] basePackageClasses() default {};
  //为所有的Feign Client设置默认配置类
  Class<?>[] defaultConfiguration() default {};
  //指定用@FeignClient注释的类列表。如果该项配置不为空,则不会进行类路径扫描
  Class<?>[] clients() default {};
  
}

我们可以看到这个注解上面添加了@Import 注解,不知道@Import 注解的参考 我之前的博文

想写Spring Boot SDK?先深入学习下@Import 注解吧

可以看到@Import 注解导入了一个配置类FeignClientsRegistrar.class , 所以核心处理逻辑都在这个类了

2. 配置类 FeignClientsRegistrar.class

这里不会贴所有源代码,防止大家看不懂,我们一步一步基于方法来分析,部分非主流程细节将省略

首先FeignClientsRegistrar 实现了接口ImportBeanDefinitionRegistrar,这个接口就是spring动态注册Bean的方法, 所以我们直接看FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions 的方法,这个方法就是源码的入口

  • ImportBeanDefinitionRegistrar
public interface ImportBeanDefinitionRegistrar {
  
  default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
		registerBeanDefinitions(importingClassMetadata, registry);
	}

	default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
	}
  
}
  • FeignClientsRegistrar
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
  
  @Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}
  
  

}

这里可以看到有两个方法registerDefaultConfigurationregisterFeignClients

  • registerDefaultConfiguration

看名字就知道是注册默认的配置类,我们简单的看看吧,这个方法并不是主流程

private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
 		// 获取注解 EnableFeignClients 上的值
		Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
		}
	}

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);
		registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}

可以看看defaultAttrs的值

image-20220210220229774

然后将FeignClientSpecification注入spring容器中

  • registerFeignClients

这里是生成接口的代理类的核心逻辑,我们重点来看看

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

		LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
  	// 获取注解 EnableFeignClients 上的值
		Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
		final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
  	// 1. 未指定EnableFeignClients注解中的clients属性,则去扫描
		if (clients == null || clients.length == 0) {
      // 获取 ClassPath扫描器
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
      //  为扫描器设置资源加载器org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
			scanner.setResourceLoader(this.resourceLoader);
      // 添加 注解类型过滤器,只过滤@FeignClient
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
      // 获取需要扫描的基础包集合
			Set<String> basePackages = getBasePackages(metadata);
      // 扫描基础包,且满足过滤条件下的接口封装成BeanDefinition加入集合candidateComponents中
			for (String basePackage : basePackages) {
				candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
			}
		}
  	// 2. 指定了EnableFeignClients注解中的clients属性
		else {
			for (Class<?> clazz : clients) {
				candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
			}
		}

		for (BeanDefinition candidateComponent : candidateComponents) {
			if (candidateComponent instanceof AnnotatedBeanDefinition) {
				// verify annotated class is an interface
				AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
				AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
				Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
				// 4. 获取 client FeignClient注解上的属性
				Map<String, Object> attributes = annotationMetadata
						.getAnnotationAttributes(FeignClient.class.getCanonicalName());

				String name = getClientName(attributes);
        // 5. 注册配置类
				registerClientConfiguration(registry, name, attributes.get("configuration"));
				// 6.生成 feign client代理类
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}
  1. EnableFeignClients 注解中没有指定client属性就去扫描包
  2. 如果EnableFeignClients 指定了client class就直接去注册这些feign client
  3. 我们看看candidateComponents里面包含的class

在这里插入图片描述

可以看到扫描到了我们定义的client:PlutusClient

但是需要注意这里的class 还是只是接口,所以我们需要生成这个接口的代理类

  1. 遍历我们扫描到的feign Client,首先获取到FeignClient注解上的属性

image-20220210222008096

  1. 通过方法registerClientConfiguration注册client的配置类
  2. 通过方法registerFeignClient 生成feign client的代理类

​ 我们进去这个方法看看

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 接口
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
		factoryBean.setBeanFactory(beanFactory);
		factoryBean.setName(name);
		factoryBean.setContextId(contextId);
		factoryBean.setType(clazz);
		BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
			factoryBean.setUrl(getUrl(beanFactory, attributes));
			factoryBean.setPath(getPath(beanFactory, attributes));
			factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
			Object fallback = attributes.get("fallback");
			if (fallback != null) {
				factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
						: ClassUtils.resolveClassName(fallback.toString(), null));
			}
			Object fallbackFactory = attributes.get("fallbackFactory");
			if (fallbackFactory != null) {
				factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
						: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
			}
			return factoryBean.getObject();
		});
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		definition.setLazyInit(true);
		validate(attributes);

		String alias = contextId + "FeignClient";
		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
		beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
		beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);

		// has a default, won't be null
		boolean primary = (Boolean) attributes.get("primary");

		beanDefinition.setPrimary(primary);

		String qualifier = getQualifier(attributes);
		if (StringUtils.hasText(qualifier)) {
			alias = qualifier;
		}

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

代码有点长,我们挑重点看

  1. 可以看到FeignClientFactoryBean 实现了FactoryBean 接口,spring中实现了FactoryBean接口的类在获取的时候都是获取的他的代理对象即getObject()方法返回的对象,所以代理对象生成的逻辑我们直接去看getObject()就好
	@Override
	public Object getObject() {
		return getTarget();
	}

	<T> T getTarget() {
    //1. 从应用上下文中获取创建 feign 客户端的上下文对象 FeignContext
		FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
				: applicationContext.getBean(FeignContext.class);
    // 2. 构建一个 Feign.Builder 对象,最终有Builder对象来生成Feign代理对象
		Feign.Builder builder = feign(context);
		// FeignClient 属性 url 没有被指定
		if (!StringUtils.hasText(url)) {
			if (!name.startsWith("http")) {
				url = "http://" + name;
			}
			else {
				url = name;
			}
			url += cleanPath();
      // 3. 通过loadBalance负载均衡并生成feign Client代理对象 
			return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
		}
    // @FeignClient 属性 url 属性被指定的情况,则不需要负载均衡
		if (StringUtils.hasText(url) && !url.startsWith("http")) {
			url = "http://" + url;
		}
		String url = this.url + cleanPath();
		Client client = getOptional(context, Client.class);
		if (client != null) {
			if (client instanceof FeignBlockingLoadBalancerClient) {
				// not load balancing because we have a url,
				// but Spring Cloud LoadBalancer is on the classpath, so unwrap
        // 因为指定了明确的服务节点url,所以这里不需要负载均衡
				client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
			}
			builder.client(client);
		}
		Targeter targeter = get(context, Targeter.class);
    // 生成最终的feign client 实例
		return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
	}
  1. 构建一个FeignContext,包含后面生成Feign需要用到的一些属性

  2. 构建一个Feign.Builder 供后续创建代理对象使用

    protected Feign.Builder feign(FeignContext context) {
      	// log 相关不用管
    		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    		Logger logger = loggerFactory.create(type);
    
    		// @formatter:off
    		Feign.Builder builder = get(context, Feign.Builder.class)
    				// required values
    				.logger(logger)
    				.encoder(get(context, Encoder.class))//编码器
    				.decoder(get(context, Decoder.class))//解码器
    				.contract(get(context, Contract.class));// 上下文
    		// @formatter:on
    		// 设置一些客户端属性 比如feign读取超时时间、连接超时时间等
    		configureFeign(context, builder);
      	// 扩展给外部使用者,外部使用者可以给Feign.Builder设置其他参数
    		applyBuildCustomizers(context, builder);
    
    		return builder;
    	}
    
  3. 通过loadBalance负载均衡并生成feign Client代理对象

我们进入loadBalance() 方法看看

protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
  	// 1.获取从spring中获取Client实现类 FeignBlockingLoadBalancerClient
		Client client = getOptional(context, Client.class);
		if (client != null) {
			builder.client(client);
      // 2. 获取Targeter 实现类 DefaultTargeter
			Targeter targeter = get(context, Targeter.class);
			return targeter.target(this, builder, context, target);
		}

		throw new IllegalStateException(
				"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
	}

我们通过debug可以看到Client 的实现类为FeignBlockingLoadBalancerClient

image-20220211102802828

那么这个类在哪里创建的呢?很明显就是就自动装配配的,我们通过搜索FeignBlockingLoadBalancerClient 并查看在哪使用最终定位到在DefaultFeignLoadBalancerConfiguration 中装配的

image-20220211103748620

很明显是通过springboot的spi自动装配的,这里就不分析了,我们继续回到主线

这里Targeter 获取到的实现类是DefaultTargeter 分析方式和上面类似就不展开了

最后生成代理对象的方法就是这里了

  • Feign.class
public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }
		// 最终根据各种配置生成一个 ReflectiveFeign 对象	
    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.java 方法
// 创建最终的feign客户端实例 : 一个 ReflectiveFeign$FeignInvocationHandler 的动态代理对象
public <T> T newInstance(Target<T> target) {
  	// MethodHandler 接口实现类默认 √ 真正的代理逻辑实现
    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)));
      }
    }
  	// factory = InvocationHandlerFactory.Default 内部类
  	// 这里 create 创建为 ReflectiveFeign 参考下面create方法
    InvocationHandler handler = factory.create(target, methodToHandler);
  	// 创建feign客户端实例的动态代理对象
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);
		// 将缺省方法处理器绑定到feign客户端实例的动态代理对象上
    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }

// InvocationHandlerFactory.Default.class
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }

代理类真正的逻辑实现是在SynchronousMethodHandler方法里面

public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        // 远程调用
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        try {
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }


Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {
      // 这个client是 Client.Default  去调用
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
      if (logLevel != Logger.Level.NONE) {
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);


    if (decoder != null)
      return decoder.decode(response, metadata.returnType());

    CompletableFuture<Object> resultFuture = new CompletableFuture<>();
    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
        metadata.returnType(),
        elapsedTime);

    try {
      if (!resultFuture.isDone())
        throw new IllegalStateException("Response handling not done");

      return resultFuture.join();
    } catch (CompletionException e) {
      Throwable cause = e.getCause();
      if (cause != null)
        throw cause;
      throw e;
    }
  }

可以看到核心实现又是委托给了实际的网络调用是在Client中调用的 在这里插入图片描述

整体的执行流程就是这样了,大致流程源码就分析完成 了

总结

总的流程来说就是当我们使用@EnableFeignClients 注解后就启动了feign的客户端扫描注册机制,然后扫描那些被@FeignClient装饰的接口,然后为这些接口生成动态代理类,这些代理类最终的代理实现是交给SynchronousMethodHandler 去实现,而真正实现远程调用又是交给了Client.Default去执行网络请求