ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

小许的技术驿站——SpringCloud-OpenFeign源码解析笔记

2021-06-30 21:02:43  阅读:302  来源: 互联网

标签:FeignClient Feign OpenFeign builder 源码 context 小许 null class


小许的技术驿站——SpringCloud-OpenFeign源码解析笔记

小弟有一个开源项目,希望大家可以多多一键三连,谢谢大家

nirvana-reborn

后续的源码解析也都会进行同步更新上去

1、什么是OpenFeign?

根据上篇博客能够看到在使用 EurekaloadBalance来进行服务调用时,都需要创建 RestTemplate组件进行 Http请求访问,但是如果每次都是用组件进行请求的话,我们在代码上面会写的很麻烦,而且也会很繁琐,所以 SpringCloud整合了 Feign客户端。而 Feign是一个声明性web服务客户端。Spring Cloud集成了EurekaSpring Cloud CircuitBreakerSpring Cloud LoadBalancer,在使用Feign时提供一个负载均衡的http客户端。并且可以解析Spring MVC注解,意思就是说,我们使用 Feign那么我们就可以不需要在手动使用 RestTemplate组件进行请求 Http接口了。

2、怎么使用OpenFeign?

我们首先改造一下我们消费者的代码。

/**
 * 开启Eureka-CLient客户端,OpenFeign客户端
 */
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class EurekaServiceBApplication {

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

    @RestController
    @Configuration
    public class ServiceBController {
    
      	/**
      	*	SayHello 请求OpenFeign组件
      	**/
        @Autowired
        SayHelloClient sayHelloClient;

        @RequestMapping(value = "/greeting/{name}", method = RequestMethod.GET)
        public String greeting(@PathVariable("name") String name) {
            return sayHelloClient.test(name);
        }
    }
}

/**
 * 声明一个SayHello OpenFeign请求客户端
 */
@FeignClient("ServiceA")
public interface SayHelloClient {
    
  	/**
  	*	基于Http请求 ServiceA 的 /test/sayHello/{test} 接口
  	**/
    @GetMapping("/test/sayHello/{test}")
    String test(@PathVariable("test") String test);
    
}

根据上面的代码我们在请求 ServiceA/test/sayHello/接口时就不需要使用 RestTemplate组件了,而且在我们开发的过程中,就可以直接把Controller层加上一个接口打包成一个Jar包进行调用即可,这样能够保证接口的请求地址与参数都不会出问题,也能够简化开发。

3、@EnableFeignClients 注解详解

小知识:在看Spring相关的代码时,应该多去关注一些注解中 @Import导入的类,在 SpringBoot中应该多多关注 xxxAutoConfiguration类。

我们在 @EnableFeignClients注解中能够看到导入了 FeignClientsRegistrar.class类,从Spring源码结构来看,一般这样的类都是该注解的关键处理类,所以我们应该仔细阅读一下。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 注册全局默认的FeignClient配置组件
		registerDefaultConfiguration(metadata, registry);
    // 注册 @FeignClient 标注的接口
		registerFeignClients(metadata, registry);
	}


}

registerDefaultConfiguration(metadata, registry)主要是根据 @EnableFeignClients注解中的 defaultConfiguration属性加载默认的全局配置类,如果没有配置就默认使用 FeignClientSpecification.class进行注册,而最主要的是如何去注册 被@FeignClient注解标注的接口。

3.1 registerFeignClients(metadata, registry);

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {	
  	//被@FeignClient标注可以注册为 SpringBean的集合
		LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
  	// 获取@EnableFeignClients的全部属性值
		Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
  	// 获取@EnableFeignClients属性中的clients属性
		final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
  	//如果没有手动配置clients属性那么就通过包路径扫描进行注册
		if (clients == null || clients.length == 0) {
      //获取根据 classpath路径进行扫描的Provider类
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
			scanner.setResourceLoader(this.resourceLoader);
      //扫描被@FeignClient注解标注的接口
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
      //获取扫描包集合
			Set<String> basePackages = getBasePackages(metadata);
			for (String basePackage : basePackages) {
				candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
			}
		}
		else {
      // 把@EnableFeignClients的clients属性class数组添加到candidateComponents集合中
			for (Class<?> clazz : clients) {
				candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
			}
		}
		// 循环注册FeignClient SpringBean
		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");
				// 获取接口上面 @FeignClient标注的注解熟悉
				Map<String, Object> attributes = annotationMetadata
						.getAnnotationAttributes(FeignClient.class.getCanonicalName());
				//获取 @FeignClient  以contextId->value->name->serviceId 进行逐步获取feign名称
				String name = getClientName(attributes);
        //注册当前FeignClient的配置类
				registerClientConfiguration(registry, name, attributes.get("configuration"));
				//注册FeignClient到Spring容器中
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
}

从上面的源码能够看到,其实 Feign的模式是和 loadbalance源码的模式是差不多的,都是一个 Client或者一个 loadbalance都会去维护一个 Spring ApplicationContext ,需要什么组件直接通过 getBean()进行获取,其配置全部都是通过配置类进行注册到 Spring上下文进行初始化与配置。

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
			Map<String, Object> attributes) {
  	// FeignClient 接口类名称
		String className = annotationMetadata.getClassName();
  	// 反射获取FeignClient 接口类
		Class clazz = ClassUtils.resolveClassName(className, null);
  	// 获取SpringBean Factory
		ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
				? (ConfigurableBeanFactory) registry : null;
		String contextId = getContextId(beanFactory, attributes);
		String name = getName(attributes);
  	//创建 FeignClientBean并且设置一些Bean的一些属性
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
		factoryBean.setBeanFactory(beanFactory);//设置Bean工厂
		factoryBean.setName(name);//设置Bean名称
		factoryBean.setContextId(contextId);//设置 Feign的上下文Id
		factoryBean.setType(clazz);//设置Bean class类型
		factoryBean.setRefreshableClient(isClientRefreshEnabled());// 设置是否自动刷新Client客户端,默认False
  	// 通过Spring BeanDefinitionBuilder 创建Bean
		BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
      // 设置当前Feign客户端的请求Url地址
			factoryBean.setUrl(getUrl(beanFactory, attributes));
      // 设置当前Feign客户端的请求地址前缀
			factoryBean.setPath(getPath(beanFactory, attributes));
      // 设置当前Feign客户端请求404时,是解析请求还是抛出异常,默认false
			factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
      //判断当前Feign客户端是否有返回处理对象,如果有就设置,当时返回的处理对象必须是SpringBean
			Object fallback = attributes.get("fallback");
			if (fallback != null) {
				factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
						: ClassUtils.resolveClassName(fallback.toString(), null));
			}
      //判断当前Feign客户端是否有返回处理对象工厂,该工厂必须要生成返回处理对象,也必须是SpringBean,如果有就设置
			Object fallbackFactory = attributes.get("fallbackFactory");
			if (fallbackFactory != null) {
				factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
						: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
			}
      // 这里是最关键的一步,获取到的对象被 FeignClientFactoryBean动态代理处理过,在调用时会自动去请求Eureka服务接口
			return factoryBean.getObject();
		});
  	// 自动注入方式,根据类型自动注入
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  	// 是否是懒加载
		definition.setLazyInit(true);
		validate(attributes);
		
  	//从BeanDefinitionBuilder中获取上面配置的 BeanDefinition
		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
  	// 设置当前的 BeanDefinition 注入类型是 接口类型
		beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
  	// 设置 feignClientsRegistrarFactoryBean 其根本是 FeignClientFactoryBean 对象,
  	// 主要是 getObject()方法获取被动态代理过的 FeignClient接口
		beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
		// 当前的beanDefinition是首先的,首先注入的
		boolean primary = (Boolean) attributes.get("primary");
		beanDefinition.setPrimary(primary);
		// 当前Feign是否有别名,默认为空
		String[] qualifiers = getQualifiers(attributes);
		if (ObjectUtils.isEmpty(qualifiers)) {
			qualifiers = new String[] { contextId + "FeignClient" };
		}
		//创建一个带有名称与别名的 Holder,其本身还是beanDefinition
		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
  	// 注册 beanDefinition
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
		// 注册一个 refreshScope 默认不创建
		registerOptionsBeanDefinition(registry, contextId);
	}

根据这段源码我们看到了,其实被 @FeignClient标注的接口,会被注册成 SpringBean而在通过 FeignClientFactoryBean进行获取 Bean的时候,获取到的 Bean对象其实是被 Feign动态代理生成过了,这时候在调用接口的时候,就会被 Feign通过动态代理的方式去请求。

3.2 FeignClientFactoryBean#getObject

@Override
public Object getObject() {
		return getTarget();
}
<T> T getTarget() {
		FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
				: applicationContext.getBean(FeignContext.class);
  	//创建一个 Feign.Builder 并且加载对应的 Feign配置文件参数
		Feign.Builder builder = feign(context);
  	//判断在 @FeignClient注解中是否配置了 url 如果没有配置那么就默认使用 @FeignClient name 名称作为请求地址
		if (!StringUtils.hasText(url)) {
			if (!name.startsWith("http")) {
				url = "http://" + name;
			}
			else {
				url = name;
			}
			url += cleanPath();
      // 生成一个 loadbalance的请求动态代理对象
			return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
		}
  	// 如果设置了 url属性,那么就根据 url设置请求地址
		if (StringUtils.hasText(url) && !url.startsWith("http")) {
			url = "http://" + url;
		}
		String url = this.url + cleanPath();
  	// 获取Eureka Client 客户端,并且判断Eureka Client是使用那种方式进行请求,默认是 FeignBlockingLoadBalancerClient.class
		Client client = getOptional(context, Client.class);
		if (client != null) {
			if (client instanceof FeignBlockingLoadBalancerClient) {
				client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
			}
			if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
				client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
			}
			builder.client(client);
		}
  	// 获取动态代理增强对象,默认是 DefaultTargeter对象
		Targeter targeter = get(context, Targeter.class);
  	// 使用 DefaultTargeter 生成一个动态代理对象
		return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}

读取配置文件配置FeignClient客户端

protected Feign.Builder feign(FeignContext context) {
  	// 从 Spring 容器中获取 logger工厂,默认是 Slf4jLogger
		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
		Logger logger = loggerFactory.create(type);
  	// 从 Spring 容器中获取 Feign.Builder ,并且配置参数初始值
		Feign.Builder builder = get(context, Feign.Builder.class)
				.logger(logger)//配置日志对象
				.encoder(get(context, Encoder.class))//配置请求参数编码器
				.decoder(get(context, Decoder.class))//配置请求参数解码器
				.contract(get(context, Contract.class));//配置SpringMVC 注解解析器
		// 根据配置文件配置FeignClient配置参数
		configureFeign(context, builder);
  	// 执行用户自己定义的创建 Feign Client构建器
		applyBuildCustomizers(context, builder);

		return builder;
	}

	private void applyBuildCustomizers(FeignContext context, Feign.Builder builder) {
    // 由此能够知道自己定义的Feign构建,必须也是SpringBean
		Map<String, FeignBuilderCustomizer> customizerMap = context.getInstances(contextId,
				FeignBuilderCustomizer.class);

		if (customizerMap != null) {
			customizerMap.values().stream().sorted(AnnotationAwareOrderComparator.INSTANCE)
					.forEach(feignBuilderCustomizer -> feignBuilderCustomizer.customize(builder));
		}
		additionalCustomizers.forEach(customizer -> customizer.customize(builder));
	}

	protected void configureFeign(FeignContext context, Feign.Builder builder) {
    //从Spring容器中获取到 Feign Client 配置文件,主要是在 application.properties文件中配置的参数
		FeignClientProperties properties = beanFactory != null ? beanFactory.getBean(FeignClientProperties.class)
				: applicationContext.getBean(FeignClientProperties.class);
		//从Spring容器中获取到当前Feign Client的全局配置
		FeignClientConfigurer feignClientConfigurer = getOptional(context, FeignClientConfigurer.class);
		setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
		// 配置文件的读取流程是 全局的配置->Default默认配置参数->clientProperties单个feign client配置参数
    // 覆盖顺序是对底层的覆盖最上层的
		if (properties != null && inheritParentContext) {
			if (properties.isDefaultToProperties()) {
        //配置FeignClient全局配置参数
				configureUsingConfiguration(context, builder);
        //配置FeignClient默认配置参数
				configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
        //针对FeignClient单个服务读取配置参数
				configureUsingProperties(properties.getConfig().get(contextId), builder);
			}
			else {
				configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
				configureUsingProperties(properties.getConfig().get(contextId), builder);
				configureUsingConfiguration(context, builder);
			}
		}
		else {
			configureUsingConfiguration(context, builder);
		}
	}

	protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
    // 整体配置全部都是从Spring容器中获取对应的Bean对象,然后放入 Feign.Builder 对象中
		Logger.Level level = getInheritedAwareOptional(context, Logger.Level.class);
		if (level != null) {
			builder.logLevel(level);
		}
		Retryer retryer = getInheritedAwareOptional(context, Retryer.class);
		if (retryer != null) {
			builder.retryer(retryer);
		}
		。。。
	
	}

	protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config,
			Feign.Builder builder) {
    // 通过用户在application.properties配置文件中的配置,进行逐步设置相关参数
		if (config == null) {
			return;
		}

		if (config.getLoggerLevel() != null) {
			builder.logLevel(config.getLoggerLevel());
		}
		//是否自动刷新feign client
		if (!refreshableClient) {
			connectTimeoutMillis = config.getConnectTimeout() != null ? config.getConnectTimeout()
					: connectTimeoutMillis;
			readTimeoutMillis = config.getReadTimeout() != null ? config.getReadTimeout() : readTimeoutMillis;
			followRedirects = config.isFollowRedirects() != null ? config.isFollowRedirects() : followRedirects;

			builder.options(new Request.Options(connectTimeoutMillis, TimeUnit.MILLISECONDS, readTimeoutMillis,
					TimeUnit.MILLISECONDS, followRedirects));
		}

		if (config.getRetryer() != null) {
			Retryer retryer = getOrInstantiate(config.getRetryer());
			builder.retryer(retryer);
		}
		。。。
	}

获取EurekaClient,并且生成动态代理对象

protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
  	// 从Spring容器中获取到EurekaClient,默认是 FeignBlockingLoadBalancerClient 对象
		Client client = getOptional(context, Client.class);
		if (client != null) {
			builder.client(client);
     	// 从Spring容器中获取 Targeter 动态代理类,默认是 DefaultTargeter
			Targeter targeter = get(context, Targeter.class);
			return targeter.target(this, builder, context, target);
		}
}
class DefaultTargeter implements Targeter {
  // 通过该代码能够看到,动态代理生成的对象还是通过Feign.Builder进行生成的
	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
			Target.HardCodedTarget<T> target) {
		return feign.target(target);
	}
}
public Feign build() {
  	// 对当前配置的FeignClient的参数根据this.capabilities集合进行增强,默认 this.capabilities 为空
    Client client = (Client)Capability.enrich(this.client, this.capabilities);
    Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);
    List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {
      return (RequestInterceptor)Capability.enrich(ri, this.capabilities);
    }).collect(Collectors.toList());
    Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);
    Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);
    Options options = (Options)Capability.enrich(this.options, this.capabilities);
    Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);
    Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);
    InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
    QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
    Factory synchronousMethodHandlerFactory = new Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
    ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
  	// 返回一个可以生成FeignClient动态代理的对象
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

public <T> T newInstance(Target<T> target) {
  	//根据 target对象创建对应方法的SpringMVC注解的MethodHandler,创建的MethodHandler是SynchronousMethodHandler
  	//所以在方法动态代理调用是,主要还是调用SynchronousMethodHandler 的invoke方法
    Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
  	// 用于存储方法与解析SpringMVC注解的MethodHandler
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
  	// 当 target 是 Object对象是,则会用到该list
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
  	// 获取当前接口所有方法集合
    Method[] var5 = target.type().getMethods();
    int var6 = var5.length;
		// 循环所有方法集合
    for(int var7 = 0; var7 < var6; ++var7) {
      Method method = var5[var7];
      // 判断当前的方法是存在于对象中还是接口中
      if (method.getDeclaringClass() != Object.class) {
        // 当前的方法在对象(Object)中
        if (Util.isDefault(method)) {
          DefaultMethodHandler handler = new DefaultMethodHandler(method);
          defaultMethodHandlers.add(handler);
          methodToHandler.put(method, handler);
        } else {
          // 当前方法在接口(interface)中
          //并且对该方法解析SpringMVC注解的nameToHandler放一起
          methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
        }
      }
    }
		// 通过动态代理构造工厂 InvocationHandlerFactory 创建一个 FeignInvocationHandler动态代理 Handler
    InvocationHandler handler = this.factory.create(target, methodToHandler);
  	// 生成一个动态代理对象
    T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
  	// 若是对象中的方法则通过默认的 DefaultMethodHandler 绑定到创建成功的proxy动态代理上面
    Iterator var12 = defaultMethodHandlers.iterator();
    while(var12.hasNext()) {
      DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
      defaultMethodHandler.bindTo(proxy);
    }
		//返回一个该 类/接口 的动态代理
    return proxy;
}

从上面的代码中可以看到,当 调用FeignClientFactoryBean#getObject方法时,其实获取到的是一个已经被 Feign使用 Java原生的动态代理进行代理过的对象即 FeignInvocationHandler对象,这时候就知道我们在Spring自动注入 Feign Client的时候其实注入的就是 FeignInvocationHandler对象,接下来,我们来看一下,当我们调用 Feign Client请求服务接口时的调用流程。

4、OpenFeign 动态代理调用Eureka服务接口

FeignInvocationHandler动态代理核心方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (!"equals".equals(method.getName())) {
        if ("hashCode".equals(method.getName())) {
          return this.hashCode();
        } else {
          																																// 主要是这里通过创建时放入的methodToHandler,根据动态代理传过																																					// 来的方法获取该方法的MethodHandler,进行调用Eureka服务
          return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
        }
     } else {
        try {
          Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return this.equals(otherHandler);
        } catch (IllegalArgumentException var5) {
          return false;
        }
    }
}

SynchronousMethodHandler方法动态代理执行源码

public Object invoke(Object[] argv) throws Throwable {
  	// 根据方法的SpringMVC注解进行解析请求,创建RequestTemplate
    RequestTemplate template = this.buildTemplateFromArgs.create(argv);
  	// 获取请求超时等参数
    Options options = this.findOptions(argv);
  	// 获取重试机制
    Retryer retryer = this.retryer.clone();

    while(true) {
      try {
        // 执行http请求调用
        return this.executeAndDecode(template, options);
      } catch (RetryableException var9) {
        // feign 重试机制
        RetryableException e = var9;
        try {
          retryer.continueOrPropagate(e);
        } catch (RetryableException var8) {
          Throwable cause = var8.getCause();
          if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
            throw cause;
          }
          throw var8;
        }

        if (this.logLevel != Level.NONE) {
          this.logger.logRetry(this.metadata.configKey(), this.logLevel);
        }
      }
    }
}
	//请求数据与解码响应数据
 Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
   //执行请求的一些Interceptor拦截器
   Request request = this.targetRequest(template);
 	 //开始请求时间
   long start = System.nanoTime();
   Response response;
   try {
     //通过 FeignBlockingLoadBalancerClient进行执行eureka接口调用
     response = this.client.execute(request, options);
     //根据response响应数据builder一下整理返回数据
     response = response.toBuilder().request(request).requestTemplate(template).build();
   } catch (IOException var12) {
     // 如果Http请求出现IOException就是通信异常,就会throw FeignException 异常
     // 在调用前已经 try catch捕获到FeignException该,并且也有相关的重试机制
     throw FeignException.errorExecuting(request, var12);
   }
		// 获取请求调用消耗时长
   long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
   if (this.decoder != null) {
     // 根据Feign的解码器将返回的数据处理成该方法的返回类型
     return this.decoder.decode(response, this.metadata.returnType());
   } else {
     // 如果没有自定义的解码器,就直接调用当前异步返回方法类型其核心还是使用默认的Decoder
     CompletableFuture<Object> resultFuture = new CompletableFuture();
     this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);
     try {
       if (!resultFuture.isDone()) {
         throw new IllegalStateException("Response handling not done");
       } else {
         return resultFuture.join();
       }
     } catch (CompletionException var13) {
       Throwable cause = var13.getCause();
       if (cause != null) {
         throw cause;
       } else {
         throw var13;
       }
     }
   }
}

根据上面的代码能够看到,其实调用的方式还是使用的 FeignBlockingLoadBalancerClientFeignBlockingLoadBalancerClient也属于 LoadBalancer组件的范畴,所以在进行Eureka服务地址选择时,还是走的 LoadBalancer的那一套东西,只不过在获取到相应的请求地址时,会生成一个 Request请求,最核心还是调用 feign.Client.Default#convertResponse方法进行http调用

5、总结

从上面的源码解析,我们能够看到,OpenFeign的整体流程是

1、先根据 @EnableFeignClients注解所在的包名进行扫描下面所有的 @FeignClient接口,并且配置相关配置信息参数

2、获取到所有标注 @FeignClient接口进行循环创建 BeanDefinitionBuilder Spring Bean

3、每个BeanDefinitionBuilder都是一个 FactoryBean所以就会有返回一个通过 JDK 动态代理生成的对象进行返回给 Spring容器,而在通过Spring注入时,就会将动态代理过后的对象注入进去。即、FeignInvocationHandler

4、在调用 Feign Client接口时通过 FeignInvocationHandler动态代理对象进行远程调用 Eureka远程服务,主要是根据 FeignBlockingLoadBalancerClient通过 LoadBalancer组件进行获取对应的服务请求地址并且组装成一个 Request,通过 feign.Client.Default#convertResponse方法进行Http调用

5、在调用结束后通过返回的数据进行 Decoder解码返回参数,返回给调用方法

自此以上 OpenFeign主要流程已全部解析完毕

标签:FeignClient,Feign,OpenFeign,builder,源码,context,小许,null,class
来源: https://blog.csdn.net/qq_38100149/article/details/118369219

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有