Feign源码阅读(一)FeignContext命名容器工厂

3,036 阅读2分钟

一、前言

学习Feign之前最好先阅读一下Ribbon源码,因为Feign依赖于Ribbon。

Ribbon源码阅读:

二、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在子容器里是非单例的? ...