从一个简单案例说起
主启动类
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class, args);
}
}
业务Service (在FeignClient上写要调用的微服务名称)
@FeignClient(name = "cloud-payment-service")
public interface PaymentFeignService {
@GetMapping("/payment/get/{id}")
CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}
从上述的简单案例中,看出,只需要在主启动类上添加@EnableFeignClients,再声明一个接口,即可完成远程调用,那么,关键就在于@EnableFeignClients
所以,就看看@EnableFeignClients 干了什么?
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}
通过上面的源码可以看出,@EnableFeignClients 通过@Import导入了一个组件,所以,又转向了FeignClientsRegistrar,可以看出,FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar
class FeignClientsRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware{
}
那么,就可以在 registerBeanDefinitions 导入一些组件,在 FeignClientsRegistrar 中干了以下两件事
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
- registerDefaultConfiguration(metadata, registry);
注册默认的配置(defaultConfiguration),这边没有写,跳过
registerFeignClients(metadata, registry); 注册Feign的客户端,主要分为如下几步:
getScanner(); 获取扫描器
# 这里重写了 判断组件是否扫描的方法
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
@Override
protected boolean isCandidateComponent(
AnnotatedBeanDefinition beanDefinition) {
boolean isCandidate = false;
if (beanDefinition.getMetadata().isIndependent()) {
if (!beanDefinition.getMetadata().isAnnotation()) {
isCandidate = true;
}
}
return isCandidate;
}
};
scanner.setResourceLoader(this.resourceLoader) 设置资源的加载器
- scanner.findCandidateComponents(basePackage) 调用扫描器,扫描所有的 FeignClient
- registerFeignClient(registry, annotationMetadata, attributes) 将FeignClient 注册到容器中
- BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class) 生成 FeignClientFactoryBean 的bean定义,将Feign的接口类型,转成了 FeignClientFactoryBean类型
那么,在真正执行远程调用的时候,将会调用 FeignClientFactoryBean 的getObject() 获取Feign接口的代理对象
@Override
public Object getObject() throws Exception {
return getTarget();
}
getTarget() 中的方法如下
this.applicationContext.getBean(FeignContext.class); 第一步,从容器中获取 FeignContext对象
# FeignContext 是在 自动注入中 FeignAutoConfiguration 中加入到容器中的
@Bean
public FeignContext feignContext() {
FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}
feign(context); 将FeignContext 转化为 Feign.Builder
loadBalance(builder, context,new HardCodedTarget<>(this.type, this.name, this.url));
- getOptional(context, Client.class); 将会从容器中取得Client类型 的 LoadBalancerFeignClient 实例,原因如下:
# 自动注入中导入的 DefaultFeignLoadBalancedConfiguration
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {}
# DefaultFeignLoadBalancedConfiguration 中
@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) {
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
clientFactory);
}
- getOptional(context, Client.class); 将会从容器中取得Client类型 的 LoadBalancerFeignClient 实例,原因如下:
targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url)) 调用Targeter对象的target方法,生成代理对象
将会调用的是DefaultTargeter,其代码为:
# DefaultTargeter#target()
feign.target(target);
# 调用feign的target方法
# feign#target()
public <T> T target(Target<T> target) {
return this.build().newInstance(target);
}
# 其调用build(),生成一个Feign的实例,最终生成的是 ReflectiveFeign
public Feign build() {
Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy);
ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}
在 newInstance中,往目标方法中,注入了一个 InvocationHandler,其真实类型是FeignInvocationHandler,将会在目标方法执行的时候,进行拦截,执行其对应的invoke方法