【源码分析-Spring-Cloud】-4.Spring Cloud Feign 实现原理


Spring Cloud Feign:【Spring Cloud学习】-4.Spring Cloud Feign 服务调用

1.Feign 是如何调用服务的?

当调用 /user/list 时,可以看到这时的 UserService 是一个代理对象,它被 jdk 动态代理。

继续执行会调用 ReflectiveFeign.FeignInvocationHandler#invoke 方法,调用路径如下

2.代理对象是如何创建的?

在 OfcFeignUserClientApplication 上添加了 @EnableFeignClients 注解,启用了 feign 功能,@EnableFeignClients 中通过 @Import 引入了 @FeignClientsRegistrar

@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 {};

}

@FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar 接口,可以动态装载 bean

的定义

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
                                    BeanDefinitionRegistry registry) {
    // 注册默认配置
    registerDefaultConfiguration(metadata, registry);
    // 注册 FeignClient
    registerFeignClients(metadata, registry);
}

registerFeignClients 扫描所有加了 @FeignClient 的类,然后再调用 registerFeignClient 方法进行注册

public void registerFeignClients(AnnotationMetadata metadata,
                                 BeanDefinitionRegistry registry) {
    // 获取一个scanner扫描对象
    ClassPathScanningCandidateComponentProvider scanner = getScanner();
    // 资源加载器,当前应用上下文
    scanner.setResourceLoader(this.resourceLoader);

    Set<String> basePackages;

    // 获取 @EnableFeignClients 的属性
    Map<String, Object> attrs = metadata
        .getAnnotationAttributes(EnableFeignClients.class.getName());
    AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
        FeignClient.class);
    // 判断是否配置了 clients 属性
    final Class<?>[] clients = attrs == null ? null
        : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
        // 如果没有配置,获取 basePackage
        scanner.addIncludeFilter(annotationTypeFilter);
        basePackages = getBasePackages(metadata);
    }
    else {
        // 否则只扫描 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)));
    }

    // 遍历 basePackages
    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");

                Map<String, Object> attributes = annotationMetadata
                    .getAnnotationAttributes(
                    FeignClient.class.getCanonicalName());

                String name = getClientName(attributes);
                // 注册 FeignClient 上的 configuration 配置
                registerClientConfiguration(registry, name,
                                            attributes.get("configuration"));
                // 注册 FeignClient
                registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }
}

registerFeignClient() 中通过 genericBeanDefinition 来构建一个 BeanDefinitionBuilder 对象,它传入一个 FeignClientFactoryBean 参数

private void registerFeignClient(BeanDefinitionRegistry registry,
                                 AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    // 通过 FeignClientFactoryBean 生成一个 BeanDefinitionBuilder 对象
    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);
    String contextId = getContextId(attributes);
    definition.addPropertyValue("contextId", contextId);
    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 = contextId + "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 进行包装
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                                                           new String[] { alias });
    // 注册到 registry 中
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

FeignClientFactoryBean 是一个工厂 bean,它实现了 FactoryBean 接口,重写 getObject 方法,自定义 bean 的初始化

@Override
public Object getObject() throws Exception {
   return getTarget();
}

getTarget 判断是否有指定url,如果指定了url,直接调用;否则使用负载均衡选择一个服务提供者。这里没有指定,所以调用 loadBalance

<T> T getTarget() {
    // 从应用上下文中获取 FeignContext
    FeignContext context = this.applicationContext.getBean(FeignContext.class);
    // 初始化 Feign.Builder
    Feign.Builder builder = feign(context);

    if (!StringUtils.hasText(this.url)) {
        // 如果没有指定url,使用负载均衡选择一个服务提供者
        if (!this.name.startsWith("http")) {
            this.url = "http://" + this.name;
        }
        else {
            this.url = this.name;
        }
        this.url += cleanPath();
        // 负载均衡调用
        return (T) loadBalance(builder, context,
                               new HardCodedTarget<>(this.type, this.name, this.url));
    }

    // 如果指定了url,直接调用对应的服务
    if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
        this.url = "http://" + this.url;
    }
    String url = this.url + cleanPath();
    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
            client = ((LoadBalancerFeignClient) client).getDelegate();
        }
        builder.client(client);
    }
    Targeter targeter = get(context, Targeter.class);
    return (T) targeter.target(this, builder, context,
                               new HardCodedTarget<>(this.type, this.name, url));
}

loadBalance 通过 targeter.target 进行代理

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
                            HardCodedTarget<T> target) {
    // 获取一个 Client 的实例
    Client client = getOptional(context, Client.class);
    if (client != null) {
        builder.client(client);
        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-netflix-ribbon?");
}

target 方法中,通过 build() 创建 ReflectiveFeign 对象,然后调用它的 newInstance 方法,生成代理对象

@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
      FeignContext context, Target.HardCodedTarget<T> target) {
   return feign.target(target);
}
public <T> T target(Target<T> target) {
    // 调用 newInstance 生成代理对象
    return this.build().newInstance(target);
}

public Feign build() {
    // 创建工厂类
    Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy);
    ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
    // 创建 ReflectiveFeign 对象
    return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory, this.queryMapEncoder);
}

调用 ReflectiveFeign#newInstance,生成 jdk 代理对象

public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
    Method[] var5 = target.type().getMethods();
    int var6 = var5.length;

    for(int var7 = 0; var7 < var6; ++var7) {
        Method method = var5[var7];
        if (method.getDeclaringClass() != Object.class) {
            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
    InvocationHandler handler = this.factory.create(target, methodToHandler);
    // 生成代理对象
    T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
    Iterator var12 = defaultMethodHandlers.iterator();

    while(var12.hasNext()) {
        DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
        defaultMethodHandler.bindTo(proxy);
    }

    return proxy;
}

这里的 factory 为 InvocationHandlerFactory,调用其 create 方法

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

所以最终生成的代理对象为 FeignInvocationHandler


文章作者: Soulballad
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Soulballad !
评论
  目录