从一个简单案例说起
主启动类
@SpringBootApplication@EnableDiscoveryClient@EnableFeignClientspublic 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 FeignClientsRegistrarimplements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware{}
那么,就可以在 registerBeanDefinitions 导入一些组件,在 FeignClientsRegistrar 中干了以下两件事
@Overridepublic 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) {@Overrideprotected 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接口的代理对象
@Overridepublic Object getObject() throws Exception {return getTarget();}
getTarget() 中的方法如下
this.applicationContext.getBean(FeignContext.class); 第一步,从容器中获取 FeignContext对象
# FeignContext 是在 自动注入中 FeignAutoConfiguration 中加入到容器中的@Beanpublic 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@ConditionalOnMissingBeanpublic 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的实例,最终生成的是 ReflectiveFeignpublic 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方法
