1.FeignClient生成代理过程

@EnableFeignClients注解

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(FeignClientsRegistrar.class)
  5. public@interfaceEnableFeignClients

我们使用@EnableFeignClients来开启feign的功能。从源码中看,该注解主要作用是通过@Import注解引入FeignClientsRegistrar类,追踪源码。

注入类FeignClientsRegistrar.class

  1. class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
  2. ResourceLoaderAware,BeanClassLoaderAware,EnvironmentAware

该接口实现了ResourceLoaderAware,BeanClassLoaderAware,EnvironmentAware接口,在解析@import注解的过程中会将感兴趣的bean注入到该类中,这里不详细展开。实现了ImportBeanDefinitionRegisterar接口,通过重写registerBeanDefinitions方法来注册默认的feign配置类和注册feignClients对应的FeignClientFactoryBean到容器当中。看一下源码:
主要的逻辑方法在下面这个方法:

  1. @Override
  2. Public void registerBeanDefinitions(AnnotationMetadatametadata,
  3. BeanDefinitionRegistryregistry){
  4. registerDefaultConfiguration(metadata,registry); // 注册@EnableFeignClients指定的默认配置类到容器
  5. registerFeignClients(metadata,registry); //注册feignClients对应的feignClientFactoryBean到容器
  6. }
  1. // 注册默认的配置类(FeignClientSpecification)
  2. Private void registerDefaultConfiguration(AnnotationMetadatametadata,
  3. BeanDefinitionRegistryregistry){
  4. Map<String,Object>defaultAttrs=metadata.getAnnotationAttributes(EnableFeignClients.class.getName(),true); // 获取默认的配置类
  5. // 如果指定了配置类,对其进行注册
  6. if(defaultAttrs!=null&&defaultAttrs.containsKey("defaultConfiguration")){
  7. Stringname;
  8. if(metadata.hasEnclosingClass()){
  9. name="default."+metadata.getEnclosingClassName();
  10. }
  11. else{
  12. name="default."+metadata.getClassName();
  13. }
  14. registerClientConfiguration(registry,name,
  15. defaultAttrs.get("defaultConfiguration")); // 注册到容器
  16. }
  17. }

在registerFeignClients这个方法中,回去扫描所有带有FeignClient注解的类,遍历解析出所有带目标注解(@FeignClient)的beanDefinition,然后调用registerFeignClient生成一个代理类,实际上每一个client接口都会生成一个代理类

  1. // 注册feignclients,实际上是其对应的工厂bean
  2. Public void registerFeignClients(AnnotationMetadatametadata,
  3. BeanDefinitionRegistryregistry){
  4. ClassPathScanningCandidateComponentProviderscanner=getScanner();
  5. scanner.setResourceLoader(this.resourceLoader);// 注解扫描器
  6. AnnotationTypeFilter annotationTypeFilter=newAnnotationTypeFilter(
  7. FeignClient.class);// 设置扫描的目标注解
  8. 省略一些代码,这里代码作用就是获取@EnableFeignClientsclients属性是否为空,确定扫描的基础包路径。(不为空时,基础包路径为指定clients class的所在包路径,为空时则为@EnableFeignClients所在包的路径)
  9. for(String basePackage:basePackages){
  10. // 解析出所有带目标注解(@FeignClient)的beanDefinition
  11. Set<BeanDefinition>candidateComponents=scanner
  12. .findCandidateComponents(basePackage);
  13. // 遍历注册
  14. for(BeanDefinition candidateComponent:candidateComponents){
  15. if(candidateComponent instanceof AnnotatedBeanDefinition){
  16. //verify annotated class is an interface //校验注解必须加载接口上
  17. AnnotatedBeanDefinition beanDefinition=(AnnotatedBeanDefinition)candidateComponent;
  18. AnnotationMetadata annotationMetadata=beanDefinition.getMetadata();
  19. Assert.isTrue(annotationMetadata.isInterface(),
  20. "@FeignClient can only be specified on an interface");
  21. Map<String,Object>attributes=annotationMetadata
  22. .getAnnotationAttributes(
  23. FeignClient.class.getCanonicalName());
  24. String name=getClientName(attributes);
  25. registerClientConfiguration(registry,name,
  26. attributes.get("configuration")); // 注册指定的配置类
  27. // 注册client(实际上为factoryBean)
  28. registerFeignClient(registry,annotationMetadata,attributes);
  29. }
  30. }
  31. }
  32. }

主要来看一下registerFeignClient方法,为每一个BeanDefinition单独生成一个FeignClientFactoryBean,而FeignClientFactoryBean就是我们生成Feign接口代理类的关键

  1. // 注册
  2. Private void registerFeignClient(BeanDefinitionRegistryregistry,
  3. AnnotationMetadataannotationMetadata,Map<String,Object>attributes){
  4. StringclassName=annotationMetadata.getClassName();
  5. // 构建FeignClientFactoryBean
  6. BeanDefinitionBuilder definition=BeanDefinitionBuilder
  7. .genericBeanDefinition(FeignClientFactoryBean.class);
  8. validate(attributes);
  9. definition.addPropertyValue("url",getUrl(attributes));
  10. definition.addPropertyValue("path",getPath(attributes));
  11. Stringname=getName(attributes);
  12. definition.addPropertyValue("name",name);
  13. definition.addPropertyValue("type",className);
  14. definition.addPropertyValue("decode404",attributes.get("decode404"));
  15. definition.addPropertyValue("fallback",attributes.get("fallback"));
  16. definition.addPropertyValue("fallbackFactory",attributes.get("fallbackFactory"));
  17. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
  18. String alias=name+"FeignClient";
  19. AbstractBeanDefinition beanDefinition=definition.getBeanDefinition();
  20. Boolean primary=(Boolean)attributes.get("primary");//has a default,won't be null
  21. beanDefinition.setPrimary(primary);
  22. // 是否指定了别名
  23. String qualifier=getQualifier(attributes);
  24. if(StringUtils.hasText(qualifier)){
  25. alias=qualifier;
  26. }
  27. BeanDefinitionHolderholder=new BeanDefinitionHolder(beanDefinition,className,
  28. newString[]{alias});
  29. // 注册factoryBean 和 别名
  30. BeanDefinitionReaderUtils.registerBeanDefinition(holder,registry);
  31. }

FeignClientFactoryBean类
FeignClientFactoryBean实现了FactoryBean,FactoryBean是一个工厂Bean,可以生成某一个类型Bean实例,它最大的一个作用是:可以让我们自定义Bean的创建过程。
springCloud FeignClient其实是利用了spring的代理工厂来生成代理类,所以这里将所有的 feignClient的描述信息 BeanDefinition设定为 FeignClientFactoryBean类型,该类又继承 FactoryBean,很明显,这是一个代理类。 在spring中, FactoryBean是一个工厂bean,用作创建代理bean,所以得出结论,feign将所有的feignClient bean包装成 FeignClientFactoryBean。扫描方法到此结束。
代理类什么时候会触发生成呢? 在spring刷新容器时,当实例化我们的业务service时,如果发现注册了FeignClient,spring就会去实例化该FeignClient,同时会进行判断是否是代理bean,如果为代理bean,则调用 FeignClientFactoryBean的 T getObject() throws Exception;方法生成代理bean。

  1. Public Object getObject()throwsException{
  2. // 获取feign的上下文
  3. FeignContext context=(FeignContext)this.applicationContext.getBean(FeignContext.class);
  4. Builder builder=this.feign(context); // 获取feign的建造者
  5. String url;
  6. if(!StringUtils.hasText(this.url)){ // 没有指定域名的,走服务发现
  7. if(!this.name.startsWith("http")){
  8. url="http://"+this.name;
  9. }else{
  10. url=this.name;
  11. }
  12. url=url+this.cleanPath();
  13. // 返回生成的代理类(负载均衡的)
  14. Return this.loadBalance(builder,context,newHardCodedTarget(this.type,this.name,url));
  15. }else{
  16. // 指定了域名的
  17. if(StringUtils.hasText(this.url)&&!this.url.startsWith("http")){
  18. this.url="http://"+this.url;
  19. }
  20. url=this.url+this.cleanPath();
  21. //生成client
  22. Client client=(Client)this.getOptional(context,Client.class);
  23. if(client!=null){
  24. //not lod balancing because we have a url,
  25. //but ribbon is on the classpath,so unwrap // 因为指定了域名地址了所以不需要负载平衡走ribbon了
  26. if(client instanceof LoadBalancerFeignClient){
  27. client=((LoadBalancerFeignClient)client).getDelegate();
  28. }
  29. builder.client(client);
  30. }
  31. // 生成代理类
  32. Targeter targeter=(Targeter)this.get(context,Targeter.class);
  33. Return targeter.target(this,builder,context,new HardCodedTarget(this.type,this.name,url));
  34. }
  35. }
  1. protected <T> T getOptional(FeignContext context, Class<T> type) {
  2. return context.getInstance(this.contextId, type);
  3. }

可以看到,最终是由FeignContext.getInstance来获取client
生成代理类则是通过
Return targeter.target(this,builder,context,new HardCodedTarget(this.type,this.name,url));
最终是由

  1. @Override
  2. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
  3. FeignContext context, Target.HardCodedTarget<T> target) {
  4. return feign.target(target);
  5. }
  1. public <T> T target(Target<T> target) {
  2. return this.build().newInstance(target);
  3. }
  4. public Feign build() {
  5. Client client = (Client)Capability.enrich(this.client, this.capabilities);
  6. Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);
  7. List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {
  8. return (RequestInterceptor)Capability.enrich(ri, this.capabilities);
  9. }).collect(Collectors.toList());
  10. Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);
  11. Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);
  12. Options options = (Options)Capability.enrich(this.options, this.capabilities);
  13. Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);
  14. Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);
  15. InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
  16. QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
  17. Factory synchronousMethodHandlerFactory = new Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
  18. ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
  19. return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
  20. }

ReflectiveFeign中实现。

下面来看一下FeignContext
FeignContext是Feign的上下文对象

  1. @Configuration
  2. @ConditionalOnClass(Feign.class)
  3. public class FeignAutoConfiguration {
  4. @Autowired(required = false)
  5. private List<FeignClientSpecification> configurations = new ArrayList<>();
  6. @Bean
  7. public HasFeatures feignFeature() {
  8. return HasFeatures.namedFeature("Feign", Feign.class);
  9. }
  10. @Bean
  11. public FeignContext feignContext() {
  12. FeignContext context = new FeignContext();
  13. //将feign的配置类设置到feign的容器当中
  14. context.setConfigurations(this.configurations);
  15. return context;
  16. }
  17. }

可以看到feign的配置类设置到feign的容器当中,而集合中的元素 正是上面我们提到的两处调用 registerClientConfiguration方法添加进去的,前后呼应。
然而,当我们引入了 sleuth之后,获取的 feignContext确是 TraceFeignClientAutoConfiguration中配置的实例 sleuthFeignContext:
image.png