Feign详解5-springcloud OpenFeign详解
前面我们分析 Feign 参数解析的整个流程,Feign 原生已经支持 Feign、JAX-RS 1/2 声明式规范, 而springCloud feign主要对netflix feign进行了增强和包装, 使之支持Spring MVC.
在前面的分析中,我们只是创建了一个客户端去联接一个服务.
GitHub github = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.options(new Request.Options(10000,10000,true))
.target(GitHub.class, "https://api.github.com");
GitHub github = HystrixFeign.builder()
//.client(new OkHttpClient())
//.decoder(new GsonDecoder())
//.contract(new JAXRSContract())
//.options( new Request.Options(5000,8500)) //options方法指定连接超时时长及响应超时时长
// .retryer( new Retryer.Default(5000,5000,3)) // retryer方法主要是指定重试策略
.target(GitHub.class, "https://api.github.com");
其中值得注意的点有:
1. 客户端类型有: Feign, HystrixFeign .
2.再调用客户端的builder()方法生成对应的Builder对象.
3. 向Builder对象中注入 encoder,decoder,options,retryer,logLevel,logger, Contract等对象.
4. 调用builder的target方法生成代理对象.
那么在分析Spring Cloud OpenFeign的源码时,因为它采用spring boot 的自动装配方案,所以我们要注意这些对象是如何自动装配进来的。
首先我们看一个简单的使用案例.
1. Spring Cloud OpenFeign 最简使用
1.1 引入 maven
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1.2 @EnableFeignClients 注解扫描包
@SpringBootApplication
@EnableFeignClients // 默认扫描 FeignApplication.class 包下 @FeignClient 注解
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class);
}
}
1.3 @FeignClient 配置
@FeignClient(name="MICROSERVICE-PROVIDER-PRODUCT",
configuration = FeignClientConfig.class,
fallbackFactory = IProductClientServiceFallbackFactory.class) // 配置要按自定义的类FeignClientConfig
public interface IProductClientService {
@RequestMapping("/product/get/{id}") //这个springMVC的调用地址要与服务提供方要求的完全一样
public Product getProduct(@PathVariable("id")long id);
@RequestMapping("/product/list")
public List<Product> listProduct() ;
@RequestMapping("/product/add")
public boolean addPorduct(Product product) ;
}
总结: 至此,可以像使用普通接口一样调用 http 了. 注意 这个IProductClientService是使用SpringMVC的注解以提供服务的,所以对应的Feign中的Contract应用支持SpringMvc的方案,而不是前面的案例中用过的JAXRSContract, BaseContract了.
2. Feign 整体装配流程分析
注意: OpenFeign 装配有两个入口:
springboot的 EnableAutoConfiguration 自动装配(spring.factories)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration
FeignAutoConfiguration
自动装配 FeignContext 和 Targeter,以及 Client 配置。FeignContext
是每个 FeignClient 的装配上下文,默认的配置是 FeignClientsConfigurationTargeter
有两种实现:一是 DefaultTargeter,直接调用 Feign.Builder; 二是 HystrixTargeter,调用 HystrixFeign.Builder,开启熔断。Client
:自动装配 ApacheHttpClient,OkHttpClient,装配条件不满足时,默认就是 Client.Default。但这些 Client 都没有实现负载均衡。
FeignRibbonClientAutoConfiguration
实现负载均衡,负载均衡是在 Client 这一层实现的。HttpClientFeignLoadBalancedConfiguration
ApacheHttpClient 实现负载均衡OkHttpFeignLoadBalancedConfiguration
OkHttpClient实现负载均衡DefaultFeignLoadBalancedConfiguration
Client.Default实现负载均衡
- org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration: 这两个自动化装配类是关于 请求和响应的压缩配置的.
@EnableFeignClients 自动扫描
@EnableFeignClients("com.yc.springcloud2.service")注入 FeignClientsRegistrar,FeignClientsRegistrar 开启自动扫描,将@EnableFeignClients配置的包下用 @FeignClient 标注的接口包装成 FeignClientFactoryBean 对象,最终通过 Feign.Builder 生成该接口的代理对象。而 Feign.Builder 的默认配置是 FeignClientsConfiguration,是在 FeignAutoConfiguration 自动注入的。
注意: 熔断与限流是 FeignAutoConfiguration 通过注入 HystrixTargeter 完成的,而负载均衡是 FeignRibbonClientAutoConfiguration 注入的。
3. Feign 自动装配
3.1 FeignAutoConfiguration类分析
3.1.1 FeignContext
// FeignAutoConfiguration 自动装配 FeignContext
@Autowired(required = false)
private List<FeignClientSpecification> configurations = new ArrayList<>();
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
FeignContext 是每个 Feign 客户端配置的上下文环境,如果我们调用一个服务的话,比如ServiceA,那么这个服务就会关联一个spring容器,FeignContext就代表一个独立的容器,关联着自己独立的一些组件,例如Logger组件、Decoder组件、Encoder组件等等。
注意: 每个 Feign 客户端除了默认 FeignClientsConfiguration,还可以自定义配置类 FeignClientSpecification,这些配置是如何注入的,会在 @EnableFeignClients 和 @FeignClient 源码分析时具体说明。
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
// FeignClientsConfiguration中会加载Encoder、Decoder、Logger等组件
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
}
FeignClientsConfiguration 是 Feign 的默认配置,可以通过 @EnableFeignClients 和 @FeignClient 修改默认配置(在程序代码中自定义的配置项优先于默认配置)。
FeignClientsConfiguration 主要配置如下:
@Configuration
public class FeignClientsConfiguration {
//解码器
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
return new OptionalDecoder(
new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
//编码器
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
public Encoder feignEncoder() {
return new SpringEncoder(this.messageConverters);
}
// Contract 适配 Spring MVC 注解
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
//重试器
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
// 一般环境都会配置feign.hystrix.enabled = true,这里直接看HystrixFeign.builder();
@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
}
...
}
在FeignClientsConfiguration类中我们已经可以看到 feign客户端builder()中要装配的对象( decoder, encoder, logger, retryer,contract ) 都准备好了.
3.1.2 Targeter: Targeter 有两种实现
再接着阅读 FeignAutoConfiguration 类中的 Targeter 有两种实现:一是 DefaultTargeter,直接调用 Feign.Builder; 二是 HystrixTargeter,调用 HystrixFeign.Builder,开启熔断。
// 如果配置了feign.hystrix.HystrixFeign 则创建HystrixTargeter
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
// 如果没有配置feign.hystrix.HystrixFeign 则创建DefaultTargeter
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
在默认情况下,feign是和hystrix整合的,feign.hystrix.HystrixFeign
会有配置,所以这里默认Targeter使用的是HystrixTargeter.
3.1.3 Client
下面接着两个客户端( HttpClient, Okhttp) 的配置:
@Configuration
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
protected static class HttpClientFeignConfiguration {
....
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(HttpClient httpClient) {
return new ApacheHttpClient(httpClient);
}
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
....
@Bean
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
ConnectionPool connectionPool,
FeignHttpClientProperties httpClientProperties) {
Boolean followRedirects = httpClientProperties.isFollowRedirects();
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.followRedirects(followRedirects).connectionPool(connectionPool)
.build();
return this.okHttpClient;
}
}
从装配条件就可以知道,它们都没有实现负载均衡。
小结: 至此 FeignAutoConfiguration 类就分析完了,它完成了 feignContext,Targeter, Client等的创建,并装配好了 encoder,decoder,contract,logger等对象
3.2 FeignRibbonClientAutoConfiguration
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@Configuration
@AutoConfigureBefore(FeignAutoConfiguration.class) //先运行 FeignAutoConfiguration
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
// 这里导入的顺序很重要,default必须放在最后
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
@Bean
@Primary
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
SpringClientFactory factory) {
return new CachingSpringLoadBalancerFactory(factory);
}
}
小结: FeignRibbonClientAutoConfiguration 实现了负载均衡。 创建CachingSpringLoadBalancerFactory时注入的参数
SpringClientFactory是一个工厂,它用于创建客户端,负载均衡器及客户端配置实例等,针对每个客户端名它创建一个Spring容器。 实际是 RibbonClientFactory,功能等同于 FeignContext,用于装配 Ribbon 的基本组件.
在 FeignRibbonClientAutoConfiguration 之上 import 了三个配置类,HttpClientFeignLoadBalancedConfiguration、OkHttpFeignLoadBalancedConfiguration、DefaultFeignLoadBalancedConfiguration。它们都是实现了负载均衡的类,打开一个研究一下, 会发现 它创建的feignClient 对象中就注入了上面创建的 CachingSpringLoadBalancerFactory对象。 这样就可以创建一个 LoadBalancerFeignClient, 从名字上可以看出它具有负载均衡功能。
@Configuration
class DefaultFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
// cachingFactory 用于组装 FeignLoadBalancer
return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
}
}
至此,一个带负载均衡功能的 FeignClient已经创建出来了。
4. 通过 @EnableFeignClients 自动扫描,完成的装配分析
@EnableFeignClients是加在我们写的程序代码的启动类上了, 从启动类中的@EnableFeignClients开始
@SpringBootApplication
@EnableFeignClients("com.yc.springcloud2.service") // 默认扫描 FeignApplication.class 包下 @FeignClient 注解
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class);
}
}
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
String[] value() default {}; //指定扫描包路径
String[] basePackages() default {}; // 包扫描路径
Class<?>[] defaultConfiguration() default {};// 默认配置
Class<?>[] basePackageClasses() default {};
....
}
总结: 从 @EnableFeignClients 的属性大致可以推断出,FeignClientsRegistrar 实现了spring 的ImportBeanDefinitionRegistrar接口,并实现了这个接口中最重要的回调方法registerBeanDefinitions,它会在spring 容器启动时扫描 basePackages 包下的所有 @FeignClient 注解的类,用 Feign.Builder 生成动态代理的 Bean 注入到 Spring 容器中。
4.1 FeignClientsRegistrar: 注册类
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 注册 @EnableFeignClients#defaultConfiguration 默认配置类
registerDefaultConfiguration(metadata, registry);
// 扫描所有的 @FeignClient 注解
registerFeignClients(metadata, registry);
}
}
4.1.2 registerDefaultConfiguration 注册 @EnableFeignClients#defaultConfiguration 默认配置类
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
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")); // 将 @EnableFeignClients 的默认配置注入到容器中。
}
}
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());
}
还记得 FeignAutoConfiguration 自动装配 FeignContext 时的 `List<FeignClientSpecification> configurations` 吗,就是将 @EnableFeignClients 和 @FeignClient 的 configuration 属性注册到 Spring 的容器中
4.1.3 registerFeignClients
registerFeignClients 将 @FeignClient 标注的接口装配成 FeignClientFactoryBean 注入到容器中.FeignClientFactoryBean\#getObject 最终会调用 feign.target 生成最终的代理对象。
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner(); //类路径扫描器
scanner.setResourceLoader(this.resourceLoader); //资源加载器
// 扫描条件: @FeignClient
Set<String> basePackages; //要扫描的包
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName()); // 只要加了EnableFeignClients注解的类
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class); //过滤出带 @FeignClient
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
...
// 扫描 basePackage 下的 @FeignClient 注解
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage);
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
// 注册 @FeignClient 的配置
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// 将该接口通过 FeignClientFactoryBean 注入到容器中
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
// 注册 FeignClientFactoryBean
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class); // beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是FeignClientFactoryBean类
...
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
...
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
至此,我们总算看到 Bean 的注册了,在`registerFeignClient()`方法中构造了一个BeanDefinitionBuilder对象,BeanDefinitionBuilder的主要作用就是构建一个AbstractBeanDefinition,AbstractBeanDefinition类最终被构建成一个BeanDefinitionHolder 然后注册到Spring中。
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class); // beanDefinition类为FeignClientFactoryBean,故在Spring获取类的时候实际返回的是FeignClientFactoryBean类
我们知道 **FeignClientFactoryBean 是 Spring 的 Bean 工厂类,那么每次在Spring Context 创建实体类的时候会调用它的`getObject()`方法。。所以在 getObject 中一定可以看到类似 feign.target 的代码。 这一步至关重要,前面我们做的只是创建好了各种组件Encoder,Decoder,Contract等等,只有在这里才利用Spring的IOC,DI完成最终的装配工作并生成代理对象. **
4.2 ****FeignClientFactoryBean.getObject()解析
这里直接分析FeignClientFactoryBean.getObject()
方法得到代理对象,这里包含着Feign动态代理的原理
@Override
public Object getObject() throws Exception {
return getTarget(); //得到代理对象
}
<T> T getTarget() {
// 1. FeignAutoConfiguration 自动装配 FeignContext, 可以类比于ribbon中的SpringClientFactory,每个服务都对应一个独立的spring容器
FeignContext context = this.applicationContext.getBean(FeignContext.class);
// builder中包含contract、logLevel、encoder、decoder、options等信息
Feign.Builder builder = feign(context);
// 2. 如果@FeignClient注解上没有指定url,说明是要用ribbon的负载均衡
if (!StringUtils.hasText(this.url)) {
if (!this.name.startsWith("http")) {
this.url = "http://" + this.name;
}
else {
this.url = this.name;
}
// 这里构建的url类似于:http://serviceA
this.url += cleanPath();
return (T) loadBalance(builder, context,
new HardCodedTarget<>(this.type, this.name, this.url));
}
// 3. url存在,不用负载均衡
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
//获取 Client,实际上是 LoadBalancerFeignClient 对象
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
client = ((LoadBalancerFeignClient) client).getDelegate();
}
builder.client(client);
}
// 4 FeignAutoConfiguration 自动装配 Targeter
Targeter targeter = get(context, Targeter.class);
// 调用 feign.target 生成动态代理
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
以上步骤小结(可以与前面 Feign详解3-工作原理 结合看) :
获取FeignContext
FeignContext context = this.applicationContext.getBean(FeignContext.class);
这个FeignContext对象代表一个服务所关联的Spring容器,它有自己独立的一些组件,如Logger, Decoder,Encoder组件等. 通过追踪 feign( context )方法可以证明以上观点:
protected Feign.Builder feign(FeignContext context) {
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.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
configureFeign(context, builder);
return builder;
}
另外, FeignContext对象是通过本文最上面的 FeignAutoConfiguration 自动装配进来的.
创建builder对象,包括了要装配的decoder,encoder,contract,….
Feign.Builder builder = feign(context);
获取 Client,实际上是 LoadBalancerFeignClient 对象, 并装配到builder中
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient) client).getDelegate();
}
builder.client(client);
}
获取Targeter对象
Targeter targeter = get(context, Targeter.class);
通过 target()创建代理对象。
(T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
总结: 至此,@FeignClient 标注的接口,最终通过 targeter.target 生成最终的代理对象。
然后再追踪Tageter的代码:
interface Targeter {
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target);
}
这个接口的实现有两种: DefaultTargeter和HystrixTargeter。
DefaultTargeter的target()方法就是 前面 [ Feign详解3-工作原理 的 “2.2 生成代理对象” ]的部分了.
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
v^v 终于到了这个位置了,其它的就可以沿着 [Feign详解3-工作原理][Feign_3-] 的 "2.2 生成代理对象" 向下看了.
HystrixTargeter的target()方法也差不多,只是加入了fallback回退处理.
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
: factory.getContextId();
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(name, context, target, builder, fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(name, context, target, builder,
fallbackFactory);
}
return feign.target(target);
}
而且它最终还是调用了 feign.target(), 殊途同归了。 哈哈,源码分析就这么简单.
最后用一张图总结Feign动态代理生成的规则:
- 生成Feign.builder(),里面包含Encoder、Decoder、Logger等组件,还有application.properties中相关的feign client配置信息
- 生成Feign.client(),默认为LoadBalancerFeignClient
- 生成默认Targter对象:DefaultTargeter或HystrixTargter
- builder、client、targter 通过JDK动态代理生成feign动态代理对象
还没有评论,来说两句吧...