OpenFeign 服务注册和调用原理

Feign 注册到容器

和 springboot 自动配置原理类似,在 springboot 启动时会扫描到 EnableFeignClients 注解,这个注解导入了一个 FeignClientsRegistrar 类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {}

FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar,所以会通过 registerBeanDefinitions 方法注入一批 BeanDefinition

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // 注册配置类(上一篇文章说过的 EnableFeignClients.defaultConfiguration 属性)
    registerDefaultConfiguration(metadata, registry);
    // 注册 feign 客户端
    registerFeignClients(metadata, registry);
}

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients
// registerFeignClients,参数应该不陌生了,一个是元注解信息,一个是 ImportBeanDefinitionRegistrar 用来注册 bean 的
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

    LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
    // 通过元注解获取到注解属性(就是 EnableFeignClients 的配置项)
    Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
    // 先看有没有配 clients
    final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        // 扫描使用了 FeignClient 注解的类 
        scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
        Set<String> basePackages = getBasePackages(metadata);
        for (String basePackage : basePackages) {
            candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
        }
    }
    else {
        // clients 指定的类
        for (Class<?> clazz : clients) {
            candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
        }
    }

    for (BeanDefinition candidateComponent : candidateComponents) {
        if (candidateComponent instanceof AnnotatedBeanDefinition) {
            ...
            registerFeignClient(registry, annotationMetadata, attributes);
        }
    }
}

实际注册 bean 的方法 registerFeignClient,里面根据配置信息构建了一个 BeanDefinition,构建的过程还是比较简单就是设置 beanName、type、lazy 等基础信息和 EnableFeignClients 配置的 fallback 等

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    // 是否延迟加载,读取的配置项,true:使用 feign 时才创建;false(默认):项目启动时创建
    if (String.valueOf(false).equals(this.environment.getProperty("spring.cloud.openfeign.lazy-attributes-resolution", String.valueOf(false)))) {
        this.eagerlyRegisterFeignClientBeanDefinition(className, attributes, registry);
    } else {
        this.lazilyRegisterFeignClientBeanDefinition(className, attributes, registry);
    }
}

// org.springframework.cloud.openfeign.FeignClientsRegistrar#eagerlyRegisterFeignClientBeanDefinition
private void eagerlyRegisterFeignClientBeanDefinition(String className, Map<String, Object> attributes, BeanDefinitionRegistry registry) {
    this.validate(attributes);
    // 构建一个 BeanDefinitionBuilder(genericBeanDefinition 方法把 bealClass 设置为 FeignClientFactoryBean)
    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
    definition.addPropertyValue("url", this.getUrl((ConfigurableBeanFactory)null, attributes));
    definition.addPropertyValue("path", this.getPath((ConfigurableBeanFactory)null, attributes));
    String name = this.getName(attributes);
    definition.addPropertyValue("name", name);
    String contextId = this.getContextId((ConfigurableBeanFactory)null, attributes);
    definition.addPropertyValue("contextId", contextId);
    
    // 省略了 BeanDefinitionBuilder 的赋值操作
    ...
    
    // 根据 beanDefinition 再构建一个 BeanDefinitionHolder
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
    // 关联 BeanDefinitionHolder 和 registry(registry 持有 BeanDefinitionHolder,后续通过 registry 创建对象)
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    
    // 方法调用了两次,但是参数不一样,一个是创建 Feign 的配置项(Request.Options,超时配置),一个是 Feign
    this.registerRefreshableBeanDefinition(registry, contextId, Request.Options.class, OptionsFactoryBean.class);
    this.registerRefreshableBeanDefinition(registry, contextId, RefreshableUrl.class, RefreshableUrlFactoryBean.class);
}

// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerRefreshableBeanDefinition
private void registerRefreshableBeanDefinition(BeanDefinitionRegistry registry, String contextId, Class<?> beanType,
        Class<?> factoryBeanType) {
    if (isClientRefreshEnabled()) {
        String beanName = beanType.getCanonicalName() + "-" + contextId;
        BeanDefinitionBuilder definitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(factoryBeanType);
        definitionBuilder.setScope("refresh");
        definitionBuilder.addPropertyValue("contextId", contextId);
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(definitionBuilder.getBeanDefinition(),
                beanName);
        // 反射创建对象(前面说了 beanClass 是 FactoryBean)
        definitionHolder = ScopedProxyUtils.createScopedProxy(definitionHolder, registry, true);
        // 注册 beanDefinition
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
    }
}

FactoryBean 这个在前面的文章有说过,这是 spring 的知识,工厂方式构建 bean 的方式:1,使用静态方法;2:使用实例方法;3:使用 FactoryBean。如果是 FactoryBean,最终的对象是 getObject 方法返回的对象作为 bean,最终的代理对象怎么生成的就不贴源码了,代码量也不多,看下最终的代理对象长啥样吧

Feign 调用流程

就是把容器中 Feign 的代理对象取出来,然后反射执行方法的过程,先对比下刚才放到单例池的代理对象和注入的对象

先判断是否是 Object 的方法,然后执行代理对象的方法,最后发送一个 http 请求,调用链如下

posted @ 2024-07-14 18:05  CyrusHuang  阅读(1)  评论(0编辑  收藏  举报