zoukankan      html  css  js  c++  java
  • 注解 @EnableFeignClients 工作原理

    概述
    在Spring cloud应用中,当我们要使用feign客户端时,一般要做以下三件事情 :

    使用注解@EnableFeignClients启用feign客户端;
    示例 :

    @SpringBootApplication
    @EnableFeignClients
    public class TestApplication {
    public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    使用注解@FeignClient 定义feign客户端 ;
    示例 : 该例子定义了一个feign客户端,将远程服务http://test-service/test/echo映射为一个本地Java方法调用。

    @FeignClient(name = "test-service", path = "/test")
    public interface TestService {
    @RequestMapping(value = "/echo", method = RequestMethod.GET)
    TestModel echo(@RequestParam("parameter") String parameter);
    }
    1
    2
    3
    4
    5
    使用注解@Autowired使用上面所定义feign的客户端 ;

    @Autowired
    TestService testService;

    public void run()
    {
    // 这里的使用本地Java API的方式调用远程的Restful接口
    TestModel dto = testService.echo("Hello,你好!");
    log.info("echo : {}", dto);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    上面的三个步骤,前两个步骤可以理解为定义feign客户端,第三步是使用所定义的feign客户端。通过调试发现,上面第三步所注入的testService是一个代理对象,如下所示 :

    testService = {$Proxy66@5502}
    "HardCodedTarget(type=TestService, name=test-service, url=http://test-service/test)"
    h = {ReflectiveFeign$FeignInvocationHandler@6924}
    target = {Target$HardCodedTarget@6930}
    dispatch = {LinkedHashMap@6931} size = 1
    0 = {LinkedHashMap$Entry@6948}
    "public abstract xxx.model.TestModel xxx.service.TestService.echo(java.lang.String)"
    1
    2
    3
    4
    5
    6
    7
    该对象会代理客户端完成远程服务方法的调用,那么,该代理对象是如何生成的 ?这篇文章,我们通过源代码解析来回答这两个问题。

    源代码解析
    源代码版本 : spring-cloud-openfeign-core-2.1.0.RELEASE , Spring Cloud Greenwich.RELEASE

    注解@EnableFeignClients:扫描和注册feign客户端bean定义
    注解@EnableFeignClients用于告诉框架扫描所有通过注解@FeignClient定义的feign客户端。它又通过注解@Import导入了类FeignClientsRegistrar( feign客户端注册器),如下所示:

    @EnableFeignClients
    => @Import(FeignClientsRegistrar.class)
    1
    2
    FeignClientsRegistrar : feign客户端注册器
    FeignClientsRegistrar实现了接口 ImportBeanDefinitionRegistrar。而ImportBeanDefinitionRegistrar的设计目的,就是被某个实现类实现,配合@Configuration注解的配置类,在配置类被处理时,用于额外注册一部分bean定义:

    对于上面的例子,使用者配置类就是 TestApplication

    public interface ImportBeanDefinitionRegistrar {

    /**
    * Register bean definitions as necessary based on the given annotation metadata of
    * the importing @Configuration class.
    * 根据使用者配置类的注解元数据注册bean定义
    * @param importingClassMetadata 使用者配置类的注解元数据
    * @param registry 当前bean定义注册表,一般指当前Spring应用上下文对象,当前Spring容器
    */
    public void registerBeanDefinitions(
    AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #registerBeanDefinitions – 注册feign客户端配置和feign客户端
    方法FeignClientsRegistrar#registerBeanDefinitions实现如下:

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
    // 注册缺省配置到容器 registry
    registerDefaultConfiguration(metadata, registry);
    // 注册所发现的各个 feign 客户端到到容器 registry
    registerFeignClients(metadata, registry);
    }
    1
    2
    3
    4
    5
    6
    7
    #registerDefaultConfiguration– 注册feign客户端缺省配置
    // 注册feign客户端的缺省配置,缺省配置信息来自注解元数据的属性 defaultConfiguration
    private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
    // 获取注解@EnableFeignClients的注解属性
    Map<String, Object> defaultAttrs = metadata
    .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

    if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
    String name;
    // 下面是对所注册的缺省配置的的命名,格式如下 :
    // default.xxx.TestApplication
    if (metadata.hasEnclosingClass()) {
    // 针对注解元数据metadata对应一个内部类或者方法返回的方法本地类的情形
    name = "default." + metadata.getEnclosingClassName();
    }
    else {
    // name 举例 : default.xxx.TestApplication
    // 这里 xxx.TestApplication 是注解@EnableFeignClients所在配置类的长名称
    name = "default." + metadata.getClassName();
    }
    registerClientConfiguration(registry, name,
    defaultAttrs.get("defaultConfiguration"));
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #registerDefaultConfiguration方法最终注册客户端缺省配置的动作交给方法#registerDefaultConfiguration执行。

    #registerClientConfiguration – 注册feign客户端配置
    // 将指定feign客户端配置configuration作为一个bean定义注册到容器:
    // bean 定义对象类型 : GenericBeanDefinition
    // bean class : FeignClientSpecification
    // bean name : default.xxx.TestApplication.FeignClientSpecification (缺省配置)
    // bean name : test-service.FeignClientSpecification (针对某个feign client 的配置)
    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());
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #registerDefaultConfiguration方法用于注册一个feign客户端配置bean,可以用于注册针对所有feign客户端的缺省配置的注册,也可以用于针对每个feign客户端的专有配置的注册。

    针对所有feign客户端的缺省配置的bean名称类似于 : default.xxx.TestApplication.FeignClientSpecification,
    针对某个名称为test-service的feign客户端的配置的bean名称类似于:test-service.FeignClientSpecification。

    #registerFeignClients – 注册各个feign客户端及其配置
    // 参数 metadata : 注解@EnableFeignClients所在配置类的注解元数据
    public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
    // 定义一个基于classpath的组件扫描器,它会根据指定的扫描位置和@EnableFeignClients注解属性
    // 找出开发人员定义的所有feign客户端,也就是那些使用了注解@FeignClient的所有接口定义
    ClassPathScanningCandidateComponentProvider scanner = getScanner();
    scanner.setResourceLoader(this.resourceLoader);

    Set<String> basePackages;

    // attrs 用于表示注解@EnableFeignClients所在配置类的注解元数据中注解@EnableFeignClients
    // 的部分
    Map<String, Object> attrs = metadata
    .getAnnotationAttributes(EnableFeignClients.class.getName());
    AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
    FeignClient.class);
    final Class<?>[] clients = attrs == null ? null
    : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
    // @EnableFeignClients 中没有指定 clients 属性的情况
    scanner.addIncludeFilter(annotationTypeFilter);
    basePackages = getBasePackages(metadata);
    }
    else {
    // @EnableFeignClients 中指定了 clients 属性的情况
    final Set<String> clientClasses = new HashSet<>();
    basePackages = new HashSet<>();
    for (Class<?> clazz : clients) {
    basePackages.add(ClassUtils.getPackageName(clazz));
    clientClasses.add(clazz.getCanonicalName());
    }
    AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
    @Override
    protected boolean match(ClassMetadata metadata) {
    String cleaned = metadata.getClassName().replaceAll("\$", ".");
    return clientClasses.contains(cleaned);
    }
    };
    scanner.addIncludeFilter(
    new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
    }

    // 使用 scanner 扫描每一个 basePackage, 获取其中的 feign 客户端定义,
    // 也就是 @FeignClient 定义的那些接口
    for (String basePackage : basePackages) {
    Set<BeanDefinition> candidateComponents = scanner
    .findCandidateComponents(basePackage);
    for (BeanDefinition candidateComponent : candidateComponents) {
    if (candidateComponent instanceof AnnotatedBeanDefinition) {
    // verify annotated class is an interface
    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
    Assert.isTrue(annotationMetadata.isInterface(),
    "@FeignClient can only be specified on an interface");

    // 获取所定义的feign客户端接口上的注解@FeignClient属性
    Map<String, Object> attributes = annotationMetadata
    .getAnnotationAttributes(
    FeignClient.class.getCanonicalName());

    String name = getClientName(attributes);
    // 将所定义的feign客户端上的配置属性作为一个bean注册到容器
    registerClientConfiguration(registry, name,
    attributes.get("configuration"));

    // 将所定义的feign客户端作为一个bean注册到容器:
    // bean 定义类型 : GenericBeanDefinition
    // bean class : FeignClientFactoryBean
    // autowire 模式 : 根据类型绑定
    // @FeignClient注解中的url,path,fallback等属性会设置为bean定义的属性
    registerFeignClient(registry, annotationMetadata, attributes);
    }
    }
    }
    }

    // 辅助工具类,从@EnableFeignClients注解属性中获取basePackages属性:
    // 参考以下@EnableFeignClients注解属性 :
    // 1. value
    // 2. basePackages
    // 3. basePackageClasses
    // 4. 配置类所在的包
    // 参数 importingClassMetadata : 使用注解@EnableFeignClients的配置类的元数据
    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
    // 注解@EnableFeignClients的属性
    Map<String, Object> attributes = importingClassMetadata
    .getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());

    Set<String> basePackages = new HashSet<>();
    for (String pkg : (String[]) attributes.get("value")) {
    if (StringUtils.hasText(pkg)) {
    basePackages.add(pkg);
    }
    }
    for (String pkg : (String[]) attributes.get("basePackages")) {
    if (StringUtils.hasText(pkg)) {
    basePackages.add(pkg);
    }
    }
    for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
    basePackages.add(ClassUtils.getPackageName(clazz));
    }

    if (basePackages.isEmpty()) {
    basePackages.add(
    ClassUtils.getPackageName(importingClassMetadata.getClassName()));
    }
    return basePackages;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    #registerFeignClients 最终注册feign客户端配置的动作交给#registerClientConfiguration完成,注册feign客户端的动作交给#registerFeignClient方法完成。

    #registerFeignClient – 注册一个feign客户端
    // 将所定义的feign客户端作为一个bean注册到容器:
    // bean 定义类型 : GenericBeanDefinition
    // bean class : FeignClientFactoryBean -- 这是一个工厂bean,而不是最终bean实例的class
    // autowire 模式 : 根据类型绑定
    // @FeignClient注解中的url,path,fallback等属性会设置为bean定义的属性
    // 参数 registry : Spring 容器
    // 参数 annotationMetadata : @FeignClient所注解的接口上的注解元数据
    // 参数 attributes : @FeignClient 注解属性信息
    private void registerFeignClient(BeanDefinitionRegistry registry,
    AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    BeanDefinitionBuilder definition = BeanDefinitionBuilder
    .genericBeanDefinition(FeignClientFactoryBean.class);
    validate(attributes);
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    definition.addPropertyValue("type", className);
    definition.addPropertyValue("decode404", attributes.get("decode404"));
    definition.addPropertyValue("fallback", attributes.get("fallback"));
    definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

    String alias = name + "FeignClient";
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

    boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null

    beanDefinition.setPrimary(primary);

    String qualifier = getQualifier(attributes);
    if (StringUtils.hasText(qualifier)) {
    alias = qualifier;
    }

    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
    new String[] { alias });
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    从上面的代码分析可知,FeignClientsRegistrar的主要作用如下 :

    注册缺省feign客户端配置bean定义;
    对于每个@FeignClient注解的feign客户端定义 :
    注册一个针对该feign客户端的配置bean定义;
    注册该feign客户端bean定义,生成bean采用工厂类FeignClientFactoryBean;
    而且,上述功能实现在FeignClientsRegistrar实现的接口ImportBeanDefinitionRegistrar所定义的方法registerBeanDefinitions中。而该方法会在应用的@EnableFeignClients注解被处理时被调用执行。具体的执行时调用栈如下所示:

    AbstractApplicationContext#invokeBeanFactoryPostProcessors
    => PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors
    => foreach BeanDefinitionRegistryPostProcessor : #postProcessBeanDefinitionRegistry
    => ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
    => #processConfigBeanDefinitions
    => ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
    => foreach ConfigurationClass : #loadBeanDefinitionsForConfigurationClass
    => #loadBeanDefinitionsFromRegistrars
    => foreach ImportBeanDefinitionRegistrar : #registerBeanDefinitions
    => FeignClientsRegistrar#registerBeanDefinitions
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    FeignClientFactoryBean生成feign客户端代理对象
    基于上面的分析,我们可以得知,开发人员所定义的feign客户端和相关配置会以bean定义的形式注册到bean容器中,这样当使用@Autowired注入一个feign客户端时,容器会使用工厂类FeignClientFactoryBean为其生成一个实例。下面我们来看其具体工作过程。

    FeignClientFactoryBean#getObject生成feign客户端代理对象
    // 该方法由接口FactoryBean约定
    @Override
    public Object getObject() throws Exception {
    return getTarget();
    }


    <T> T getTarget() {
    // 从应用上下文中获取创建 feign 客户端的上下文对象 FeignContext
    // FeignContext 针对每个feign客户端定义会生成一个不同的 AnnotationConfigApplicationContext,
    // 这些应用上下文的parent都设置为当前应用的主应用上下文
    // 参考 : FeignAutoConfiguration
    FeignContext context = applicationContext.getBean(FeignContext.class);
    // 为目标feign客户端对象构建一个 builder,该builder最终生成的目标feign客户端是一个
    // 动态代理,使用 InvocationHandler : ReflectiveFeign$FeignInvocationHandler
    Feign.Builder builder = feign(context);

    if (!StringUtils.hasText(this.url)) {
    // @FeignClient 属性 url 属性没有指定的情况
    // 根据属性 name , path 拼装一个 url,
    // 这种通常是需要在多个服务节点之间进行负载均衡的情况
    if (!this.name.startsWith("http")) {
    url = "http://" + this.name;
    }
    else {
    url = this.name;
    }
    // 方法cleanPath()加工属性path,使其以/开头,不以/结尾
    url += cleanPath();
    // 这里形成的url格式类似 : http://test-service/test
    // 其中 test-service 是服务名,不是服务所在节点的IP,主机名或者域名
    // 函数loadBalance 做如下动作 :
    // 1. 将builder和一个LoadBalancerFeignClient bean实例关联起来
    // 2. 使用一个HystrixTargeter将builder和一个 HardCodedTarget bean实例关联起来
    // 这里 HardCodedTarget 表示对应 url 为 http://test-service/test 的远程服务(可能
    // 包含多个服务方法)
    // 3. 生成最终的feign client 实例 : ReflectiveFeign$FeignInvocationHandler 的动态代理对象,
    // 使用 InvocationHandler : ReflectiveFeign$FeignInvocationHandler。
    // 每个远程服务方法会对应到一个@FeignClient注解的接口方法上(依据方法上的注解进行匹配)
    return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
    this.name, url));
    }

    // @FeignClient 属性 url 属性被指定的情况
    // 这种通常是明确指出了服务节点的url的情况,实际上不需要负载均衡
    if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
    this.url = "http://" + this.url;
    }
    String url = this.url + cleanPath();
    // 将builder和一个LoadBalancerFeignClient bean实例关联起来
    Client client = getOptional(context, Client.class);
    if (client != null) {
    if (client instanceof LoadBalancerFeignClient) {
    // not load balancing because we have a url,
    // but ribbon is on the classpath, so unwrap
    // 因为指定了明确的服务节点url,所以这里不需要负载均衡,
    // 所以这里尽管client是LoadBalancerFeignClient,所以
    // 实际上可以获取其所代理的对象作为最终的client,
    // 相当于去掉了LoadBalancerFeignClient这层的代理功能
    client = ((LoadBalancerFeignClient)client).getDelegate();
    }
    builder.client(client);
    }
    // 使用一个HystrixTargeter将builder和一个 HardCodedTarget bean实例关联起来
    Targeter targeter = get(context, Targeter.class);
    // 生成最终的feign client 实例 : ReflectiveFeign$FeignInvocationHandler 的动态代理对象,
    // 使用 InvocationHandler : ReflectiveFeign$FeignInvocationHandler。
    // 每个远程服务方法会对应到 一个@FeignClient注解的接口方法上(依据方法上的注解进行匹配)
    return (T) targeter.target(this, builder, context, new HardCodedTarget<>(
    this.type, this.name, url));
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    方法FeignClientFactoryBean#feign – 创建feign客户端构建器
    protected Feign.Builder feign(FeignContext context) {
    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    Logger logger = loggerFactory.create(this.type);

    // 从上下文获取一个 Feign.Builder 上,
    // 并从上下文获得 Encoder, Decoder, Contract 设置到该 builder 上
    Feign.Builder builder = get(context, Feign.Builder.class)
    // required values
    .logger(logger)
    .encoder(get(context, Encoder.class))
    .decoder(get(context, Decoder.class))
    .contract(get(context, Contract.class));

    // 对 builder 进行其他属性设置
    configureFeign(context, builder);

    return builder;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    方法FeignClientFactoryBean#loadBalance – 生成具备负载均衡能力的feign客户端
    为feign客户端构建器绑定负载均衡客户端,绑定目标服务端点,并生成最终的feign客户端实例。

    // 对builder设置负载均衡客户端,绑定到目标服务端点,构建最终的feign客户端对象
    protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
    HardCodedTarget<T> target) {
    // 从上下文context获取一个Client,缺省是 LoadBalancerFeignClient
    Client client = getOptional(context, Client.class);
    if (client != null) {
    // 将client设置到builder上
    builder.client(client);
    // 从上下文中获取一个 targeter,缺省是一个 HystrixTargeter
    Targeter targeter = get(context, Targeter.class);
    // 上面获取得到的 targeter 会根据 builder 的类型决定如何将 target
    // 绑定到 builder 并设置有关的其他属性和功能,然后生成最终的feign客户端对象
    return targeter.target(this, builder, context, target);
    }

    throw new IllegalStateException(
    "No Feign Client for loadBalancing defined. Did you forget to include " +
    "spring-cloud-starter-netflix-ribbon?");
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    从上面分析可以看出,缺省情况下,所使用的feign客户端构建器类为Feign.Builder,并且Targeter是一个HystrixTargeter。HystrixTargeter#target方法的参数builder为Feign.Builder时,会直接调用该builder的target方法,如下所示 :

    class HystrixTargeter implements Targeter {
    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
    Target.HardCodedTarget<T> target) {
    if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
    return feign.target(target);
    }
    // ... 省略其他代码
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    接下来再来看Feign.Builder#target是如何工作的。

    // 执行构建并且创建相应的feign客户端实例
    public <T> T target(Target<T> target) {
    return build().newInstance(target);
    }

    // 构建过程,最终根据各种配置生成一个 ReflectiveFeign 对象
    public Feign build() {
    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
    new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
    logLevel, decode404, closeAfterDecode, propagationPolicy);
    ParseHandlersByName handlersByName =
    new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
    errorDecoder, synchronousMethodHandlerFactory);
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    然后再看ReflectiveFeign#newInstance方法。

    // 创建最终的feign客户端实例 : 一个 ReflectiveFeign$FeignInvocationHandler 的动态代理对象
    @Override
    public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
    if (method.getDeclaringClass() == Object.class) {
    continue;
    } else if (Util.isDefault(method)) {
    // 对于每个缺省方法,使用 DefaultMethodHandler
    DefaultMethodHandler handler = new DefaultMethodHandler(method);
    defaultMethodHandlers.add(handler);
    methodToHandler.put(method, handler);
    } else {
    // 对于每个对应服务功能端点的方法,缺省使用nameToHandler获取的MethodHandler,缺省是
    // SynchronousMethodHandler
    methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
    }
    // 创建feign客户端实例 ReflectiveFeign$FeignInvocationHandler,
    // 该对象包含了上面所创建的methodToHandler,用于对应各个开发者定义的@FeignClient接口方法
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 创建feign客户端实例的动态代理对象
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
    new Class<?>[] {target.type()}, handler);

    // 将缺省方法处理器绑定到feign客户端实例的动态代理对象上
    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    从上面的分析我们不难看出,为什么最终注入的testService最终是一个ReflectiveFeign$FeignInvocationHandler动态代理实例了。

    总结
    从上面的分析可以看出,当我们使用注解@EnableFeignClients 时,相当于启用了feign客户端定义的扫描和注册机制,从而可以发现开发人员通过注解@FeignClient定义的feign客户端,并最终作为bean定义注册到容器中。而通过@Autowired自动装配注解,这些feign客户端会以ReflectiveFeign$FeignInvocationHandler动态代理的形式被注入到使用方。该feign客户端包含了对每个接口方法的处理器MethodHandler,接口缺省方法对应DefaultMethodHandler,服务功能端点方法对应SynchronousMethodHandler。

    参考文章
    Spring Cloud feign客户端执行流程概述
    ---------------------
    作者:九九八八
    来源:CSDN
    原文:https://blog.csdn.net/andy_zhang2007/article/details/86680622
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    Neko's loop HDU-6444(网络赛1007)
    Parameters
    SETLOCAL
    RD / RMDIR Command
    devenv 命令用法
    Cannot determine the location of the VS Common Tools folder.
    'DEVENV' is not recognized as an internal or external command,
    How to change Visual Studio default environment setting
    error signing assembly unknown error
    What is the Xcopy Command?:
  • 原文地址:https://www.cnblogs.com/softidea/p/11099429.html
Copyright © 2011-2022 走看看