侧边栏壁纸
博主头像
Leokoの小破站博主等级

行动起来,活在当下

  • 累计撰写 14 篇文章
  • 累计创建 10 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Feign 源码分析

Leoko
2024-01-06 / 0 评论 / 0 点赞 / 63 阅读 / 40416 字

1. 配置类

image-20240125144712234

这里有五个配置类,但是初步阅读源码的话其实只需要关注第二个和第五个配置类就行。

1.1 FeignAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class,
        FeignEncoderProperties.class })
@Import(DefaultGzipDecoderConfiguration.class)
public class FeignAutoConfiguration {
    
    @Autowired(required = false)
    private List<FeignClientSpecification> configurations = new ArrayList<>();
​
    @Bean
    public HasFeatures feignFeature() {
        return HasFeatures.namedFeature("Feign", Feign.class);
    }
​
    @Bean
    public FeignContext feignContext() {
        FeignContext context = new FeignContext();
        context.setConfigurations(this.configurations);
        return context;
    }
    
    @Configuration(proxyBeanMethods = false)
    @Conditional(FeignCircuitBreakerDisabledConditions.class)
    protected static class DefaultFeignTargeterConfiguration {
​
        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter() {
            return new DefaultTargeter();
        }
​
    }
    
    ......
}

这个配置类主要关注 FeignContext 和在开启 Feign 熔断开关情况下的用来生成代理对象的 Targeter

1.2 FeignLoadBalancerAutoConfiguration

Feign 中关于负载均衡的配置:

@ConditionalOnClass(Feign.class)
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter({ BlockingLoadBalancerClientAutoConfiguration.class, LoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
@Import({ HttpClientFeignLoadBalancerConfiguration.class, OkHttpFeignLoadBalancerConfiguration.class,
		HttpClient5FeignLoadBalancerConfiguration.class, DefaultFeignLoadBalancerConfiguration.class })
public class FeignLoadBalancerAutoConfiguration {

}

这个配置类仅仅是使用 @Import 注解导入多个配置类,导入的配置类都根据条件在声明负载均衡要使用到的 ClientBean,这里先看它的默认配置类。

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(LoadBalancerProperties.class)
class DefaultFeignLoadBalancerConfiguration {

	@Bean
	@ConditionalOnMissingBean
	@Conditional(OnRetryNotEnabledCondition.class)
	public Client feignClient(LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties,
			LoadBalancerClientFactory loadBalancerClientFactory) {
		return new FeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient, properties,
				loadBalancerClientFactory);
	}

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
	@ConditionalOnBean(LoadBalancedRetryFactory.class)
	@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
			matchIfMissing = true)
	public Client feignRetryClient(LoadBalancerClient loadBalancerClient,
			LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
			LoadBalancerClientFactory loadBalancerClientFactory) {
		return new RetryableFeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient,
				loadBalancedRetryFactory, properties, loadBalancerClientFactory);
	}

}

在重试开关关闭的情况下,默认注入的 Client 类型是 FeignBlockingLoadBalancerClient,开启的开启下类型是 RetryableFeignBlockingLoadBalancerClient,怎么样,是不是和 loadBalancer 很像。

2. @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 {};
    
    /**
	 * 标有 @FeignClient 注解的类
	 */
    Class<?>[] clients() default {}
}

注解里面导入了一个 FeignClientsRegistrar,看名字应该是 ImportBeanDefinitionRegistrar 的实现类。

1.2 FeignClientsRegistrar

主要看它的 registerBeanDefinitions 方法。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    
    @Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}
}

1.2.1 registerDefaultConfiguration - 全局

注册默认配置(和 loadBalancerLoadBalancerClientConfigurationRegistrar 类似),为每个 Feign 客户端注册指定默认配置(即注解中 defaultConfiguration 指定的配置类)。

private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);

    if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
        String name;
        if (metadata.hasEnclosingClass()) {
            name = "default." + metadata.getEnclosingClassName();
        }
        else {
            name = "default." + metadata.getClassName();
        }
        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());
}

注意 registerClientConfiguration 方法中注册的 BeanDefinition 类型是 FeignClientSpecification,看名字是不是很熟悉,和 loadBalancer 类似,与 NamedContextFactory 相关,起到服务隔离的作用。后续会在自动配置类见到。

1.2.2 registerFeignClients - 单独

前面的 registerDefaultConfiguration 是为每个 Feign 客户端添加统一配置,那这个就是为每个 Feign 客户端添加属于自己的配置。

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

    LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
    Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
    final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
        Set<String> basePackages = getBasePackages(metadata);
        for (String basePackage : basePackages) {
            candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
        }
    }
    else {
        for (Class<?> clazz : clients) {
            candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
        }
    }

    for (BeanDefinition candidateComponent : candidateComponents) {
        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 = getClientName(attributes);
            registerClientConfiguration(registry, name, attributes.get("configuration"));
            // 注册Feign 客户端
            registerFeignClient(registry, annotationMetadata, attributes);
        }
    }
}
  • 如果 @EnableFeignClients 注解上属性 clients 对应的值为空, 则进行包扫描,扫描出包含 @FeignClient 的类,包扫描的路径:value -> basePackages -> basePackageClasses,如果这几个属性对应的值都为空,则取 @EnableFeignClients 注解所在类的包。

  • 如果不为空,则只取 clients 值对应的类。

  • 候选类取出完成后,先判断是否是接口,因为 @FeignClient 注解只能加在接口上。

  • 解析Feign 客户端中 @FeignClient 注解的相关属性,然后对客户端进行专属配置。registerClientConfiguration 前面讲过,主要注意一下 registerFeignClient 方法。为扫描到的每一个 Feign 客户端接口注册一个 beanDefinition 实例,其中 beanClassFeignClientFactoryBean

FactoryBean 是什么?

Spring 中,对于 Bean 的类型,一般有两种设计:普通 Bean 、工厂 Bean 。普通 Bean 就是我们平常使用的 @Component@Bean 这种方法创建的 Bean;但是考虑到一些特殊的设计:Bean的创建需要指定一些策略,或者依赖特殊的场景来分别创建,也或者一个对象的创建过程太复杂。这种情况下,如果还是使用普通的创建 Bean 方式,可能无法实现,所以 Spring 提供了 FactoryBean 来使用工厂方法创建对象。

public interface FactoryBean<T> {
    // 返回创建的对象
    @Nullable
    T getObject() throws Exception;

    // 返回创建的对象的类型(即泛型类型)
    @Nullable
    Class<?> getObjectType();

    // 创建的对象是单实例Bean还是原型Bean,默认单实例
    default boolean isSingleton() {
        return true;
    }
}

FactoryBean 本身是一个接口,如果 Bean 实现了 FactoryBean 接口,则它本身将不再是一个普通的 Bean ,不会在实际的业务逻辑中起作用,而是由创建的对象来起作用。创建 Bean 的过程由接口中的方法 getObject 实现,且 FactoryBean 生产 Bean 的机制是延迟生产。 当使用 @Autowried 或者其他方式注入 Feign 客户端的实例时,就会调用这个 getObject() 方法获取。

下面有一个简单的例子,根据这个例子来 debug 源码。

@FeignClient(name = "user-service")
public interface UserFeign {

    @GetMapping("/user")
    User getUser(@RequestParam("userId") String userId);
}

@Component
public class xxxx {
    @Autowried
    private UserFeign userFeign;
}

1.2.2.1 FeignClientFactoryBean

当在某个类注入 UserFeign 的时候,就会调用 FeignClientFactoryBeangetObject 方法生成对应的代理对象。

// FeignClientFactoryBean
public Object getObject() {
    return getTarget();
}

<T> T getTarget() {
    // 获取上下文对象
    FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
        : applicationContext.getBean(FeignContext.class);
    Feign.Builder builder = feign(context);

    if (!StringUtils.hasText(url)) {
        // logger....
        if (!name.startsWith("http")) {
            url = "http://" + name;
        }
        else {
            url = name;
        }
        url += cleanPath();
        return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
    }
    if (StringUtils.hasText(url) && !url.startsWith("http")) {
        url = "http://" + url;
    }
    String url = this.url + cleanPath();
    Client client = 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);
    }

    applyBuildCustomizers(context, builder);

    Targeter targeter = get(context, Targeter.class);
    return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
1. FeignContext

IOC 容器中获取 FeignContextFeignContext 继承了 NamedContextFactory,用来统一维护 Feign 中各个 Feign 客户端相互隔离的上下文。注册的地方则是在 FeignAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class,FeignEncoderProperties.class })
public class FeignAutoConfiguration {
    
    @Autowired(required = false)
	private List<FeignClientSpecification> configurations = new ArrayList<>();
    
    @Bean
	public FeignContext feignContext() {
		FeignContext context = new FeignContext();
		context.setConfigurations(this.configurations);
		return context;
	}
}

FeignContext 的初始化就用到了前面提到过的 FeignClientSpecification

2. url 判断

如果 @FeignClient 注解上的 url 属性为空,则走负载均衡,生成有负载均衡的代理类。

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

例如现在有两个服务 business-serviceorder-servicebusiness-service 使用 Feign 调用 order-service 的接口,对应的 Feign 客户端上注解配置为:

@FeignClient(value = "user-service")

那么按照上面的逻辑,最终生成的 urlhttp://order-service。如果 @FeignClient 中还指定是 path 的值的话,最终生成的 url就为 http://order-service 拼接上 path 属性对应的值。

3. 负载均衡生成的代理类

进入 loadBalance 方法:

protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        applyBuildCustomizers(context, builder);
        Targeter targeter = get(context, Targeter.class);
        return targeter.target(this, builder, context, target);
    }

    throw new IllegalStateException(
        "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
}

protected <T> T getOptional(FeignContext context, Class<T> type) {
    return context.getInstance(contextId, type);
}

首先从当前 Feign 客户端的 AnnotationConfigApplicationContext 上下文中取出对应的类为 ClientBean,并为当前的 Feign 客户端使用,注入的地方在前面讲到的 1.2 章节处,默认的类型是 FeignBlockingLoadBalancerClient

public interface Client {
    Response execute(Request var1, Options var2) throws IOException;
}

Client 是一个接口,里面仅包含一个方法,就是发送请求,和 LoadBalancerClient 类似。

Targeter 默认的类型是 DefaultTargeter,我们跟进看一下生成代理对象的 target 方法:

// DefaultTargeter
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) {
    // 调用 Feign.Builder 的 target 方法
    return feign.target(target);
}
// Feign.Builder
public <T> T target(Target<T> target) {
    return this.build().newInstance(target);
}

public Feign build() {
   .....
    InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
    // 参数的编码器,接口含有参数注解@QueryMap时使用
    QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
    // handlersByName将所有参数进行封装,并提供解析接口方法的逻辑
    ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

ReflectiveFeign 的构造方法有三个参数:

  • handlersByName: 将builder所有参数进行封装,并提供解析接口方法的逻辑;

  • invocationHandlerFactoryJava 动态代理的工厂;

  • queryMapEncoder:参数的编码器,接口含有参数注解@QueryMap时使用。

最后调用 newInstance 方法真正生成代理对象:

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 handler = new DefaultMethodHandler(method);
            defaultMethodHandlers.add(handler);
            methodToHandler.put(method, handler);
        } else {
            methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
        }
    }
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
                                         new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
        defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}
3.1 ParseHandlersByNam

解析接口方法上的注解,并将其转换成对应的处理器(handler)。而其中又用到了 Contarct 接口来解析接口中的方法,生成方法的元数据。

// ParseHandlersByName
public Map<String, MethodHandler> apply(Target target) {
    // 调用 BaseContract 的 parseAndValidateMetadata 方法
    List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
    .....
}

// BaseContract
public List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType) {
    final MethodMetadata data = new MethodMetadata();
    .....
    final Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
    for (final Method method : targetType.getMethods()) {
        // 方法过滤
        if (method.getDeclaringClass() == Object.class ||
            (method.getModifiers() & Modifier.STATIC) != 0 ||
            Util.isDefault(method)) {
            continue;
        }
        // 调用子类 SpringMvcContract
        final MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
        ......
        result.put(metadata.configKey(), metadata);
    }
    return new ArrayList<>(result.values());
}

// SpringMvcContract
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
    processedMethods.put(Feign.configKey(targetType, method), method);
    // 回到 BaseContract
    return super.parseAndValidateMetadata(targetType, method);
}

// BaseContract
protected MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
    ...... // 构造接口中方法对应的 MethodMetadata
    if (targetType.getInterfaces().length == 1) {
        processAnnotationOnClass(data, targetType.getInterfaces()[0]);
    }
    processAnnotationOnClass(data, targetType);
    // 处理方法上的注解
    for (final Annotation methodAnnotation : method.getAnnotations()) {
        processAnnotationOnMethod(data, methodAnnotation, method);
    }
    .....
}

注意 BaseContractnew MethodMetadata() 的地方,有个属性名为 template 的对象在初始化是就被设置为 RequestTemplate ,而 FeignClient 的请求发送就是通过这个类实现的。

public final class MethodMetadata implements Serializable {
    private final RequestTemplate template = new RequestTemplate();
    
    MethodMetadata() {
   	   template.methodMetadata(this);
 	}
}

但是这里有一点要注意哦,我们来看下 MethodMetadata 初始化后其中的 RequestTemplate对象:

image-20240303102959755

虽然其中的 RequestTemplate 已被创建完成,但是它的 toString 方法现在确有异常提示。这是为什么呢?

public String toString() {
     return request().toString();
}

 public Request request() {
     if (!this.resolved) {
         throw new IllegalStateException("template has not been resolved.");
     }
     return Request.create(this.method, this.url(), this.headers(), this.body, this);
 }

原来如此,toString 方法里面实际调用的 Request 对象的 toString 方法,且构造 Request 前还有一个判断条件 resolved,只有等 RequestTemplate 被处理完成后,才能正确显示。

那什么时候 RequestTemplate 才能被处理完成呢?其实要等到代理类被调用的时候(即调用 UserFeigngetUser 方法时),才能被处理完成,具体讲解放到了后面分析代理类调用过程的章节。

先把断点打到 SpringMvcContract 类,看下 processedMethods 存放的是什么:

image-20240301230109333

根据内容可以知道 processedMethods 这个 Map 中的数据:key 为接口类名+方法名+参数类型,value 为对应的 Method 对象。

然后我们看下 processAnnotationOnClassprocessAnnotationOnMethod 分别干了什么:

// SpringMvcContract
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
    RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
    if (classAnnotation != null) {
        LOG.error("Cannot process class: " + clz.getName()
                  + ". @RequestMapping annotation is not allowed on @FeignClient interfaces.");
        throw new IllegalArgumentException("@RequestMapping annotation not allowed on @FeignClient interfaces");
    }
}

processAnnotationOnClass 是用来检查标有 @FeignClient 注解的接口是否含有 @RequestMapping 注解,如果有的话,直接抛出异常,因为这个 FeignClient 是用来调用其他服务的,自身服务如果有的 @RequestMapping 注解的话,相当于自身服务提供接口,这是不合理的。

// SpringMvcContract
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
    .........
    // 获取接口中包含 @RequestMapping 相关注解的方法
    RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
    .........
    // 为 RequestTemplate 设置方法请求类型
    data.template().method(Request.HttpMethod.valueOf(methods[0].name()));

    // path
    checkAtMostOne(method, methodMapping.value(), "value");
    if (methodMapping.value().length > 0) {
        String pathValue = emptyToNull(methodMapping.value()[0]);
        if (pathValue != null) {
            pathValue = resolve(pathValue);
            // Append path from @RequestMapping if value is present on method
            if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
                pathValue = "/" + pathValue;
            }
            data.template().uri(pathValue, true);
            if (data.template().decodeSlash() != decodeSlash) {
                data.template().decodeSlash(decodeSlash);
            }
        }
    }
    ......
}

这段代码就是处理 @RequestMapping 注解中各个属性,最终都是将属性对应的值放到 RequestTemplate 中。

将方法转换为 MethodMetadata 后,就需要为每个方法设置对应的 MethodHandler

// ParseHandlersByName
public Map<String, MethodHandler> apply(Target target) {
    List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
    Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
    for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        // 针对不同参数设置不同的 buildTemplate
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate =
              new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
        }
        result.put(md.configKey(), factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
    }
}

比如例子中使用的是 @RequestParam,则为 BuildTemplateByResolvingArgs;如果使用的是 @RequestBody,则为 BuildEncodedTemplateFromArgs

接下来看一下生成 MethodHandler 的逻辑,使用了一个 factory 来创建:

// SynchronousMethodHandler.Factory
public MethodHandler create(Target<?> target,
                                MethodMetadata md,
                                RequestTemplate.Factory buildTemplateFromArgs,
                                Options options,
                                Decoder decoder,
                                ErrorDecoder errorDecoder) {
    return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,logLevel, md, buildTemplateFromArgs, options, decoder,errorDecoder, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
}

直接是 new 了一个 SynchronousMethodHandler 对象。

apply 方法执行完毕后,回到 newInstance 方法中:

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>();
    ......
    // 生成代理对象
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}

终于看到真正生成代理对象的地方了,而且用的是 JDK 动态代理。

public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
    return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}

我们看一下最终生成的 Proxy 对象,在其他 Bean 中注入 UserFeign 的就是这个对象:

image-20240302170133611

3.调用过程

代理对象如何生成的前面已经分析过了,接下来看一下调用过程。既然已经知道是 JDK 动态代理生成的了,那就直接跳转到方法调用:

// FeignInvocationHandler
static class FeignInvocationHandler implements InvocationHandler {

    ......
    private final Map<Method, MethodHandler> dispatch;
    
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("equals".equals(method.getName())) {
            try {
                Object otherHandler =
                    args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
                return equals(otherHandler);
            } catch (IllegalArgumentException e) {
                return false;
            }
        } else if ("hashCode".equals(method.getName())) {
            return hashCode();
        } else if ("toString".equals(method.getName())) {
            return toString();
        }

        return dispatch.get(method).invoke(args);
    }
}

equals 方法需要做一下判断,hashCodetoString 方法不做代理。最后一行用了一个 dispatch 对象来真正执行,那这个 dispatch 对象是什么呢?其实就是前面 newInstance 方法中的 methodToHandler,这里就是从这个 Map 取出方法对应的 MethodHandler,调用它的 invoke 方法。而前面已经分析过方法对应的 MethodHanlder 默认的类型是 SynchronousMethodHandler

 public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        try {
          retryer.continueOrPropagate(e);
        } catch (RetryableException th) {
          Throwable cause = th.getCause();
          if (propagationPolicy == UNWRAP && cause != null) {
            throw cause;
          } else {
            throw th;
          }
        }
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }


0

评论区