一、前言
学习Feign之前最好先阅读一下Ribbon源码,因为Feign依赖于Ribbon。
Ribbon源码阅读:
- SpringClientFactory命名容器工厂:juejin.cn/post/687554…
- Ribbon如何实现服务定制化配置:juejin.cn/post/687592…
- Ribbon请求流程解读及实现:juejin.cn/post/687672…
二、FeignContext命名容器工厂
FeignClient使用案例
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@FeignClient(name = "trade-service")
public interface StockClient {
@RequestMapping(value = "/getStockByMpId/{mpId}", method = RequestMethod.GET)
ApiResponse<Stock> getStock(@PathVariable("mpId") Long mpId);
}
@RestController
public class ExampleController {
@Autowired
private StockClient stockClient;
@RequestMapping("/seckill/{mpId}")
@ResponseBody
public ApiResponse<Stock> getStock(@PathVariable("mpId") Long mpId) {
return stockClient.getStock(mpId);
}
}
FeignContext
FeignContext继承了NamedContextFactory,和Ribbon的SpringClientFactory一样,都是为了创建Spring子容器。
public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
public FeignContext() {
super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}
@Nullable
public <T> T getInstanceWithoutAncestors(String name, Class<T> type) {
return BeanFactoryUtils.beanOfType(getContext(name), type);
}
@Nullable
public <T> Map<String, T> getInstancesWithoutAncestors(String name, Class<T> type) {
return getContext(name).getBeansOfType(type);
}
}
- 重要构造函数,之前已经学习过SpringClientFactory,这里通过一个单元测试复习一下,不过多阐述
@Autowired
private FeignContext feignContext;
private static final String serviceName = "trade-service";
@Test
public void test01() {
// 构造参数一:默认注入子容器的配置类
FeignClientsConfiguration defaultConfig = feignContext.getInstance(serviceName, FeignClientsConfiguration.class);
System.out.println(defaultConfig);// org.springframework.cloud.openfeign.FeignClientsConfiguration@296e281a
// 构造参数二:PropertySource的name
AbstractEnvironment env = feignContext.getInstance(serviceName, AbstractEnvironment.class);
Iterator<PropertySource<?>> iterator = env.getPropertySources().iterator();
if (iterator.hasNext()) {
System.out.println(iterator.next().getName()); // feign
}
// 构造参数三:PropertySource里的配置的key,value是子容器名(一般是服务名称,如果设置FeignClient注解里的contextId,就是contextId)
String property = env.getProperty("feign.client.name");
System.out.println(property); // trade-service
}
- getInstanceWithoutAncestors:只从子容器寻找bean,返回bean实例
- getInstancesWithoutAncestors:只从子容器寻找bean,返回Map<bean名称,bean实例>
三、FeignClientsConfiguration子容器默认注入的配置
重点看一下FeignClientsConfiguration,给子容器默认注入了哪些bean
- Encoder、Decoder编码解码器
@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(ObjectProvider<AbstractFormWriter> formWriterProvider) {
return springEncoder(formWriterProvider);
}
- Contract:默认实现类SpringMvcContract,解析SpringMVC的注解如:@RequestMapping。如果要使用Feign自己的注解如@RequestLine,那就要注入feign.Contract.Default实例。
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) {
return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
- FormattingConversionService:参数转换,比如时间类型转换等等
@Bean
public FormattingConversionService feignConversionService() {
FormattingConversionService conversionService = new DefaultFormattingConversionService();
for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) {
feignFormatterRegistrar.registerFormatters(conversionService);
}
return conversionService;
}
- Retryer:Feign自己的重试机制,默认不重试
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
- FeignLoggerFactory:Feign自己的日志工厂
@Bean
@ConditionalOnMissingBean(FeignLoggerFactory.class)
public FeignLoggerFactory feignLoggerFactory() {
return new DefaultFeignLoggerFactory(this.logger);
}
- FeignClientConfigurer:用于配置"是否可以从配置文件定制化FeignClient",默认是
@Bean
@ConditionalOnMissingBean(FeignClientConfigurer.class)
public FeignClientConfigurer feignClientConfigurer() {
return new FeignClientConfigurer() {
};
}
- Feign.Builder:关键,用于构建ReflectiveFeign,这里是个原型对象,非单例
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
- 当集成hystrix时,Feign.Builder是实现会是HystrixFeign,虽然同样是@ConditionalOnMissingBean,但是@ConditionalOnProperty(name = "feign.hystrix.enabled")的优先级较高,会优先取HystrixFeign.builder()
@Configuration(proxyBeanMethods = false)
@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();
}
}
四、总结
通过FeignContext命名容器工厂的构造函数和默认注入的配置类,对Feign先有个大致的概念。现在会有几个疑问:
- 为什么IRule、ServerList、ILoadBalancer这些Ribbon里的抽象在FeignContext子容器的默认配置里没有找到?
- 为什么Feign.Builder在子容器里是非单例的? ...