源码篇--SpringCloud-OpenFeign--代理类的生成

发布时间:2024年01月12日


前言

我们知道SpringCloud-OpenFeign 是通过jdk 的动态代理为接口生成代理对象,从而由代理对象完成请求的访问,下面具体看下在Spring 中 是如何完成对接口的代理的;


一、bean 定义的注册:

1.1 启动类开启feign 接口:

通常在启动类中我们需要添加 @EnableFeignClients 注解来开启feign 接口的扫描;

在这里插入图片描述
在 EnableFeignClients 注解中定义了一些属性可供我们进行设置:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}

  • value(),basePackages(),basePackageClasses() 定义扫描的包或者路径,如果我们不在@EnableFeignClients 则默认扫描启动类所在包及其子包;
  • defaultConfiguration() feign 客户端的 默认配置类,指定一个或多个配置类,以便为所有 @FeignClient 接口提供一些默认配置。这些配置可能包括编码器、解码器、拦截器等。
  • clients 直接指定一组 Feign 客户端接口类,而不通过包扫描来查找。通常用于精确控制哪些接口需要被注册为 Feign 客户端。

1.2 feign 接口的扫描:

在EnableFeignClients 类中 通过import 导入的FeignClientsRegistrar 完成了feign 接口的扫描:

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {}

FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,意味着它可以向spring 容器中注册 bean 定义,从而在spring 创建一些bean 的时候就可以根据bean 定义来生成相应的bean;

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
	// 注册 EnableFeignClients  中设置的 defaultConfiguration 注册 bean  定义
	
  this.registerDefaultConfiguration(metadata, registry);
  // 根据路径扫描 得到被 FeignClient 注解修饰的接口类,注册每个 FeignClient 中定义的 configuration
  //  然后为每个FeignClient  注册bean 定义
  this.registerFeignClients(metadata, registry);
}

1) EnableFeignClients 中defaultConfiguration 定义:

rivate void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
     Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
     if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
     	// 如果 设置了defaultConfiguration   
         String name;
         if (metadata.hasEnclosingClass()) {
             name = "default." + metadata.getEnclosingClassName();
         } else {
             name = "default." + metadata.getClassName();
         }
		// 最终生成defaultConfiguration  的bean bean 名称为
		// default.被@EnableFeignClients 注解修饰类全路径.FeignClientSpecification
	
         this.registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
     }

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

2 ) FeignClient 接口bean 定义:

通过在 EnableFeignClients 定义的路基进行扫描 ,过滤出来被@FeignClient 修饰的类

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet();
     Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
     Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
     if (clients != null && clients.length != 0) {
         Class[] var12 = clients;
         int var14 = clients.length;

         for(int var16 = 0; var16 < var14; ++var16) {
             Class<?> clazz = var12[var16];
             candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
         }
     } else {
     	// 扫描器扫描
         ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
         scanner.setResourceLoader(this.resourceLoader);
         // 扫描 被FeignClient 注解修饰的类
         scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
         Set<String> basePackages = this.getBasePackages(metadata);
         Iterator var8 = basePackages.iterator();
		// 遍历添加到 candidateComponents 集合
         while(var8.hasNext()) {
             String basePackage = (String)var8.next();
             candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
         }
     }
	// 遍历扫描到的类 进行bean 注册的行医
     Iterator var13 = candidateComponents.iterator();

     while(var13.hasNext()) {
         BeanDefinition candidateComponent = (BeanDefinition)var13.next();
         if (candidateComponent instanceof AnnotatedBeanDefinition) {
             AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
             AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
             Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
             Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
             String name = this.getClientName(attributes);
             // 注册每个FeignClient 接口定义的 configuration 配置类 
             this.registerClientConfiguration(registry, name, attributes.get("configuration"));
               // 注册每个FeignClient
             this.registerFeignClient(registry, annotationMetadata, attributes);
         }
     }

 }

registerClientConfiguration 注册每个FeignClient 接口定义的 configuration 配置类 :

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
	// BeanDefinitionBuilder  属性设置
   BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
    builder.addConstructorArgValue(name);
    builder.addConstructorArgValue(configuration);
    // bean 定义注册
    registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition());
}

FeignClient feign 接口的bean定义注册:
通过构建FeignClientFactoryBean 对象 进行注册

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    Class clazz = ClassUtils.resolveClassName(className, (ClassLoader)null);
    ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory)registry : null;
    String contextId = this.getContextId(beanFactory, attributes);
    String name = this.getName(attributes);
    // 构建FeignClientFactoryBean  对象并对其属性进行设置
    FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
    factoryBean.setBeanFactory(beanFactory);
    factoryBean.setName(name);
    factoryBean.setContextId(contextId);
    factoryBean.setType(clazz);
    factoryBean.setRefreshableClient(this.isClientRefreshEnabled());
    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
    	// 设置 @FeignClient 设置的url ,path ,decode404,fallback ,fallbackFactory 属性
    	// url  和 path  可以使用类似于 "${xxx} 的占位符,在创建bean 的过程中会对其进行解析
        factoryBean.setUrl(this.getUrl(beanFactory, attributes));
        factoryBean.setPath(this.getPath(beanFactory, attributes));
        factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
        Object fallback = attributes.get("fallback");
        if (fallback != null) {
            factoryBean.setFallback(fallback instanceof Class ? (Class)fallback : ClassUtils.resolveClassName(fallback.toString(), (ClassLoader)null));
        }

        Object fallbackFactory = attributes.get("fallbackFactory");
        if (fallbackFactory != null) {
            factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class)fallbackFactory : ClassUtils.resolveClassName(fallbackFactory.toString(), (ClassLoader)null));
        }

        return factoryBean.getObject();
    });
    definition.setAutowireMode(2);
    definition.setLazyInit(true);
    this.validate(attributes);
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    beanDefinition.setAttribute("factoryBeanObjectType", className);
    beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
    boolean primary = (Boolean)attributes.get("primary");
    beanDefinition.setPrimary(primary);
    String[] qualifiers = this.getQualifiers(attributes);
    if (ObjectUtils.isEmpty(qualifiers)) {
        qualifiers = new String[]{contextId + "FeignClient"};
    }

    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    this.registerOptionsBeanDefinition(registry, contextId);
}

至此就完成了 @FeignClient 客户端的扫描以及bean 的定义 ,后续spring 会依照bean 定义去创建bean,因为 FeignClientFactoryBean 实现 了 FactoryBean 所以在创建bean 的时候时,是通过调用其内部的getObject() 获取对象,然后再将bean 对象放入到单例池中;

二、feign bean 的生成:

feign 的生成主要通过FeignClientFactoryBean getObject() 进行:

2.1.FeignContext bean :

FeignContext是Spring Cloud OpenFeign中的一个类,它是一个专门为Feign客户端设置的特定上下文(ApplicationContext的一个子类)。FeignContext的主要作用是允许每个Feign客户端拥有自己独立的应用上下文,这样它们就可以有自己的配置(例如编码器、解码器和拦截器),而不是使用全局的默认配置。

FeignContext 的加载是在 spring.factories FeignAutoConfiguration 类中进行的

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
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
public FeignContext feignContext() {
     FeignContext context = new FeignContext();
     context.setConfigurations(this.configurations);
     return context;
 }

2.2.FeignClientFactoryBean 实例生成:

FeignClientFactoryBean 通过 getTarget() 方法 创建代理类并进行返回

public Object getObject() {
 return this.getTarget();
}

<T> T getTarget() {
	// 从容器中获取 FeignContext  的bean
    FeignContext context = this.beanFactory != null ? (FeignContext)this.beanFactory.getBean(FeignContext.class) : (FeignContext)this.applicationContext.getBean(FeignContext.class);
    // 创建组装feign 客户端的配置,这里还会为每个客户端创建子容器,保证每个客户端的独立性
    Feign.Builder builder = this.feign(context);
    if (!StringUtils.hasText(this.url)) {
        if (LOG.isInfoEnabled()) {
            LOG.info("For '" + this.name + "' URL not provided. Will try picking an instance via load-balancing.");
        }

        if (!this.name.startsWith("http")) {
            this.url = "http://" + this.name;
        } else {
            this.url = this.name;
        }

        this.url = this.url + this.cleanPath();
        return this.loadBalance(builder, context, new Target.HardCodedTarget(this.type, this.name, this.url));
    } else {
        if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
            this.url = "http://" + this.url;
        }

        String url = this.url + this.cleanPath();
        Client client = (Client)this.getOptional(context, Client.class);
        if (client != null) {
            if (client instanceof FeignBlockingLoadBalancerClient) {
                client = ((FeignBlockingLoadBalancerClient)client).getDelegate();
            }

            if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
                client = ((RetryableFeignBlockingLoadBalancerClient)client).getDelegate();
            }

            builder.client(client);
        }

        this.applyBuildCustomizers(context, builder);
        //  调用 target 方法完成代理对象的创建
        Targeter targeter = (Targeter)this.get(context, Targeter.class);
        return targeter.target(this, builder, context, new Target.HardCodedTarget(this.type, this.name, url));
    }
}

2.2.1 this.feign(context) feign 客户端的配置:

此方法配置 feign 客户端的 log 日志输出,编解码,使用的协议等;

protected Feign.Builder feign(FeignContext context) {
	// log 日志实现类的获取
 FeignLoggerFactory loggerFactory = (FeignLoggerFactory)this.get(context, FeignLoggerFactory.class);
    Logger logger = loggerFactory.create(this.type);
    // 链式调用 设置 编解码 ,contract 协议
    Feign.Builder builder = ((Feign.Builder)this.get(context, Feign.Builder.class)).logger(logger).encoder((Encoder)this.get(context, Encoder.class)).decoder((Decoder)this.get(context, Decoder.class)).contract((Contract)this.get(context, Contract.class));
    // 设置 系统默认的配置文件,客户端自定义的配置文件
    this.configureFeign(context, builder);
    return builder;
}

在对 Feign.Builder 对象链式设置里,如果发现当前feign 客户端还没有创建上下文,会先进行上下文的创建:

protected <T> T get(FeignContext context, Class<T> type) {
	// 从当前feign 客户端的容器中,获取到对应的 bean  (Feign.Builder,Decoder,Encoder,Contract)
    T instance = context.getInstance(this.contextId, type);
    if (instance == null) {
        throw new IllegalStateException("No bean found of type " + type + " for " + this.contextId);
    } else {
        return instance;
    }
}

getInstance 获取当前feign 客户端容器内的bean:

public <T> T getInstance(String name, Class<T> type) {
	// 根据当前feign 客户端定义 id 获取当前feign 客户端的容器,这里如果feign 客户端的容器还没创建则会先进行创建
    AnnotationConfigApplicationContext context = this.getContext(name);

    try {
    	// 在当前feign 客户端 容器内获取bean
        return context.getBean(type);
    } catch (NoSuchBeanDefinitionException var5) {
        return null;
    }
}

this.getContext 当前客户端容器获取:

protected AnnotationConfigApplicationContext getContext(String name) {
    if (!this.contexts.containsKey(name)) {
    	// 容器内没有当前客户端的子容器 
        synchronized(this.contexts) {
            if (!this.contexts.containsKey(name)) {
            	// createContext 创建子容器 并放入到spring 的大容器中
                this.contexts.put(name, this.createContext(name));
            }
        }
    }
	// 返回当前客户端的子容器
    return (AnnotationConfigApplicationContext)this.contexts.get(name);
}

createContext 客户端子容器的创建:

protected AnnotationConfigApplicationContext createContext(String name) {
	// 创建一个 context 上下文
  AnnotationConfigApplicationContext context;
    if (this.parent != null) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        if (this.parent instanceof ConfigurableApplicationContext) {
            beanFactory.setBeanClassLoader(((ConfigurableApplicationContext)this.parent).getBeanFactory().getBeanClassLoader());
        } else {
            beanFactory.setBeanClassLoader(this.parent.getClassLoader());
        }

        context = new AnnotationConfigApplicationContext(beanFactory);
        context.setClassLoader(this.parent.getClassLoader());
    } else {
        context = new AnnotationConfigApplicationContext();
    }
	//  在当前子容器内 注册属于自己的  configuration 配置文件
    if (this.configurations.containsKey(name)) {
        Class[] var9 = ((Specification)this.configurations.get(name)).getConfiguration();
        int var4 = var9.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Class<?> configuration = var9[var5];
            context.register(new Class[]{configuration});
        }
    }
	
    Iterator var10 = this.configurations.entrySet().iterator();

    while(true) {
        Map.Entry entry;
        do {
            if (!var10.hasNext()) {
                context.register(new Class[]{PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType});
                context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName, Collections.singletonMap(this.propertyName, name)));
                if (this.parent != null) {
                	// 设置spring 容器  为父容器
                    context.setParent(this.parent);
                }

                context.setDisplayName(this.generateDisplayName(name));
                // 刷新子容器 去创建bean 
                context.refresh();
                return context;
            }

            entry = (Map.Entry)var10.next();
        } while(!((String)entry.getKey()).startsWith("default."));

        Class[] var12 = ((Specification)entry.getValue()).getConfiguration();
        int var13 = var12.length;

        for(int var7 = 0; var7 < var13; ++var7) {
            Class<?> configuration = var12[var7];
            context.register(new Class[]{configuration});
        }
    }
}

2.2.2 configureFeign 对当前feign客户端进行配置:

protected void configureFeign(FeignContext context, Feign.Builder builder) {
 FeignClientProperties properties = this.beanFactory != null ? (FeignClientProperties)this.beanFactory.getBean(FeignClientProperties.class) : (FeignClientProperties)this.applicationContext.getBean(FeignClientProperties.class);
   FeignClientConfigurer feignClientConfigurer = (FeignClientConfigurer)this.getOptional(context, FeignClientConfigurer.class);
   this.setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
   if (properties != null && this.inheritParentContext) {
       if (properties.isDefaultToProperties()) {
       		// feign 客户端定义的配置类解析
           this.configureUsingConfiguration(context, builder);
           // 系统默认的配置解析
           this.configureUsingProperties((FeignClientProperties.FeignClientConfiguration)properties.getConfig().get(properties.getDefaultConfig()), builder);
           // 配置的客户端解析
           this.configureUsingProperties((FeignClientProperties.FeignClientConfiguration)properties.getConfig().get(this.contextId), builder);
       } else {
           this.configureUsingProperties((FeignClientProperties.FeignClientConfiguration)properties.getConfig().get(properties.getDefaultConfig()), builder);
           this.configureUsingProperties((FeignClientProperties.FeignClientConfiguration)properties.getConfig().get(this.contextId), builder);
           this.configureUsingConfiguration(context, builder);
       }
   } else {
       this.configureUsingConfiguration(context, builder);
   }

}

这里进行简单的说明:

  • configureUsingConfiguration 解析通过 @FeignClient 配置的configuration 的配置类;

  • configureUsingProperties:解析feign 默认的配置 比如 default 配置:

在这里插入图片描述

  • configureUsingProperties:最后一个解析通过配置文件定义给某个客户端的配置,这里的applicationname2 对应某个@FeignClient 定义的contextId 或者 name
    在这里插入图片描述
    在这里插入图片描述

2.2.3 target 方法代理对象的生成:

最终通过Feign 下的 Builder 类中的 target 方法生成代理对象:这里只展示部分代码

public <T> T target(Target<T> target) {
 return this.build().newInstance(target);
}

public Feign build() {
    super.enrich();
    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(this.client, this.retryer, this.requestInterceptors, this.responseInterceptor, this.logger, this.logLevel, this.dismiss404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
    ReflectiveFeign.ParseHandlersByName handlersByName = new ReflectiveFeign.ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
    return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}
  • .build() 方法对代理类的一些属性进行配置 其中SynchronousMethodHandler 在我们需要调用远程服务时会 通过 invoke 方法组装http 请求 并最终发起http 请求访问;
  • newInstance(target) 生成代理对象并返回;

总结

OpenFeign 客户端是通过 FeignClientFactoryBean 进行代理对象的生成,每个feign 客户端都会创造属于自己的子容器,并且将自身的配置文件作为bean 进行注册;我们在application.yml 进行的配置会覆盖掉我们在 @FeignClient 的配置;

文章来源:https://blog.csdn.net/l123lgx/article/details/135485268
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。