feign【1】相关中间件的整合

269 阅读3分钟

这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战

前言

在前面我们知道了:

  • 我们最终通过IOC注入到容器中的feign实例,是:

    • 通过ReflectiveFeign.FeignInvocationHandler代理的对象,方法的代理是SynchronousMethodHandler
  • feign的请求,是通过client去发送的,而这个client是在FeignLoadBalancerAutoConfiguration中通过Import来注入的bean。

附:spring-cloud-openfeign-core中的spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration

自动配置bean的流程:

graph TD
RibbonAutoConfiguration-->FeignRibbonClientAutoConfiguration-->FeignLoadBalancerAutoConfiguration,构造client,由于被覆盖因此没有效果-->FeignAutoConfiguration

请求client的bean配置

我们知道,在SpringBoot中要看自动装配,第一可查看的点就是spring.factories中的配置。

client相关的是org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration

这个自动配置中没有定义bean,而是通过Import来导入的:

@ConditionalOnClass(Feign.class)
@ConditionalOnBean(BlockingLoadBalancerClient.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancerConfiguration.class,
		OkHttpFeignLoadBalancerConfiguration.class,
		DefaultFeignLoadBalancerConfiguration.class })
class FeignLoadBalancerAutoConfiguration {

}

前面也说过了,如果没有额外的配置采用的是下面的这个。

@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancerConfiguration {

   @Bean
   @ConditionalOnMissingBean
   public Client feignClient(BlockingLoadBalancerClient loadBalancerClient) {
      return new FeignBlockingLoadBalancerClient(new Client.Default(null, null),
            loadBalancerClient);
   }

}

注意这里的proxyBeanMethods = false,含义就是每次取该bean的时候都会调用这个方法,相当于是原型bean的含义。

这里的BlockingLoadBalancerClient应该也是自动配置的bean,上下文中并没有,因此我们来看看上面FeignLoadBalancerAutoConfiguration中的@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)

负载均衡ribbon的配置

在SpringCloud中,ribbon负责的部分就是负载均衡了,因此这里应该是和负载均衡相关的。

@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
      matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@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);
   }

   @Bean
   @Primary
   @ConditionalOnMissingBean
   @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
   public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
         SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
      return new CachingSpringLoadBalancerFactory(factory, retryFactory);
   }

   @Bean
   @ConditionalOnMissingBean
   public Request.Options feignRequestOptions() {
      return LoadBalancerFeignClient.DEFAULT_OPTIONS;
   }

}

前面的import和上面的client一个意思,我们知道如果没有配其他的http请求包,取最下面默认的即可。

这里的bean,通过ide可以看到这个org.springframework.retry.support.RetryTemplate依赖项里是有的,因此CachingSpringLoadBalancerFactory的bean是下面这个:

public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
      SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
   return new CachingSpringLoadBalancerFactory(factory, retryFactory);
}

而import里是这个,这里的CachingSpringLoadBalancerFactory上面FeignRibbonClientAutoConfiguration里给我们解决了:

@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
      SpringClientFactory clientFactory) {
   return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
         clientFactory);
}

注意这里的DefaultFeignLoadBalancerConfigurationDefaultFeignLoadBalancedConfiguration都是类型为Client的bean,且

条件都是**@ConditionalOnMissingBean**,因此这里我们注入的Client是LoadBalancerFeignClient,前面的FeignLoadBalancerAutoConfiguration事实上没有做任何bean的装配。

此时bean构造方法中有两个类我们还没找到:

  • LoadBalancedRetryFactory
  • SpringClientFactory

RibbonAutoConfiguration

该类类头:

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
      name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
      AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
      ServerIntrospectorProperties.class })

LoadBalancedRetryFactory

这个类其实都和feign没啥关系了,包都在org.springframework.cloud.client.loadbalancer中了。

这里可以看到这个接口的bean在两个包中有autoConfiguration:

  • org.springframework.cloud.netflix.ribbon->RibbonAutoConfiguration
  • org.springframework.cloud.client.loadbalancer-->LoadBalancerAutoConfiguration

这里根据debug,可以看到是在RibbonAutoConfiguration中的bean。

@Bean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(
      final SpringClientFactory clientFactory) {
   return new RibbonLoadBalancedRetryFactory(clientFactory);
}

这里也和SpringClientFactory有关。

SpringClientFactory

其实这个也好找,就在和上面的同一个类中:

@Bean
public SpringClientFactory springClientFactory() {
   SpringClientFactory factory = new SpringClientFactory();
   factory.setConfigurations(this.configurations);
   return factory;
}

而这里的configurations,声明是这样的:

@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();

接着我们就要找这个RibbonClientSpecification在容器中注入的地方了。

Trace这个代码,发现在RibbonClientConfigurationRegistrar中,作为BeanDefinition注入:

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

这个类在注解**@RibbonClients**,@RibbonClient中被import,该注解在RibbonAutoConfiguration恰好被引入。

事实上这个注解在框架代码中还有一处被使用:RibbonEurekaAutoConfiguration