这是我参与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);
}
注意这里的DefaultFeignLoadBalancerConfiguration和DefaultFeignLoadBalancedConfiguration都是类型为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