Spring Cloud组件源码之OpenFeign源码分析

" Spring 到底是春天的来临万物复苏,还是春转夏的干燥又炎热呢?"  Spring的来临让JavaEE走向了另一个高度。便捷的开发,完美的生态。物极必反,学习Spring的成本越来越低,导致Java程序员越来越密集,越来越廉价…… 那么又有多少 Springer 耐下性子去研究Spring生态的架构和底层实现的细节呢??

版本信息如下:

Spring:5.3.23
Spring Boot:2.6.13
Spring Cloud:3.1.5
Spring Cloud Openfeign:3.1.5
Feign:11.3.0

正文:

既然是研究Spring Cloud源码,那么没有Spring、Spring Boot的前置知识,只会让你永远迈不出第一步。下面是对于Spring Cloud的架构图。

既然是学习OpenFeign源码,我们第一步是不是应该清楚Openfeign的架构,由上图我们得知Spring Cloud 组件名 承上启下,向下兼容组件,向上实现规范和扩展。那么把Spring Cloud Openfeign带入进去就很好理解了,向下兼容Feign这个组件,向上实现规范和扩展。

反观,我们连Feign这个组件的原理都不知道,就去学习Spring Cloud Openfeign那怎么学得懂呢?下面是Feign这个组件的架构思想图。一言以蔽之:简化Http客户端的操作,兼容众多Http客户端框架,向上暴露出接口+注解+配置的简易操作方式。

先学会使用,再来研究其源码,Spring Cloud Feign的使用很简单,其实单独使用Feign这个组件跟其大同小异。接口+注解的使用方法,底层帮开发者自动去调用服务名为deptmanagecloud-provider其中的/provider/pt接口。

//@Service
@FeignClient(value = "deptmanagecloud-provider")
public interface FeignConsumer {
    @GetMapping("/provider/pt")
    String provider();
}

那么,接下来就是去研究其源码,到底如何帮开发者自动去调用的过程。

项目中要使用Spring Cloud OpenFeign就需要使用@EnableFeignClients注解。 所以这将是我们的入手点。

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

此注解配合@Configuration注解一起使用,一般是配合@SpringBootApplication注解(也是一个@Configuration)使用,而@EnableFeignClients注解又使用了@Import注解。所以需要找到FeignClientsRegistrar类的registerBeanDefinitions回掉方法。

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


    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");
        // 扫描当前目录下所有的@FeignClient类。
        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));
            }
        }

        // 遍历当前目录下所有的@FeignClient类生成的BeanDefinition
        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);
                registerClientConfiguration(registry, name, attributes.get("configuration"));
                // 给当前目录下所有的@FeignClient类生成的BeanDefinition添加Feign动态代理后的代理Class
                registerFeignClient(registry, annotationMetadata, attributes);
            }
        }
    }

}

Spring Cloud Openfein的核心原理大致就在这里了,所以对以上做个总结:

  1. 使用@EnableFeignClients注解,Spring底层会解析@Import注解,最终会回掉registerBeanDefinitions方法,此方法中调用registerFeignClients
  2. registerFeignClients方法中会去扫描当前路径下所有的类,如果类上带有@FeignClient注解就会生成BeanDefition。
  3. 遍历所有带有@FeignClient注解的类,反射拿到@FeignClient注解的元数据信息,然后调用registerFeignClient方法
  4. registerFeignClient方法会解析带有@FeignClient注解元数据信息当作燃料往Feign里面填充,然后Feign会去动态代理创建出带有@FeignClient注解的类的动态代理类。

看到这里,能力比较强的读者就能明白,Spring Cloud Openfeign作为中间层,向上实现规范和扩展接口,向下兼容组件本身。拿这里举例子,Spring Cloud Openfein使用Spring的扩展点@Import来解析@FeignClient注解,把@FeignClient注解元数据信息作为燃料,填充给Feign组件本身,Feign组件获取到燃料后动态代理带有@FeignClient注解的类。在后续的依赖注入和方法调用都是走代理对象,此时我们只需要把Feign组件中动态代理的流程和代理回掉弄明白即可。

那么,接着看到registerFeignClient方法的实现。

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
    Map<String, Object> attributes) {
    String className = annotationMetadata.getClassName();
    Class clazz = ClassUtils.resolveClassName(className, null);
    ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
    ? (ConfigurableBeanFactory) registry : null;
    String contextId = getContextId(beanFactory, attributes);
    String name = getName(attributes);
    FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
    factoryBean.setBeanFactory(beanFactory);
    factoryBean.setName(name);
    factoryBean.setContextId(contextId);
    factoryBean.setType(clazz);
    factoryBean.setRefreshableClient(isClientRefreshEnabled());
    // 这里是Spring的一个扩展点,最终创建Bean对象的时候,会回掉此处。
    BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
        factoryBean.setUrl(getUrl(beanFactory, attributes));
        factoryBean.setPath(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(), null));
        }
        Object fallbackFactory = attributes.get("fallbackFactory");
        if (fallbackFactory != null) {
            factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
                : ClassUtils.resolveClassName(fallbackFactory.toString(), null));
        }
        return factoryBean.getObject();
    });

    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

}

这里很简单,拿到了@FeginClient注解的元数据信息,创建了FeignClientFactortyBean对象,把数据全部注入到FeignClientFactortyBean对象中,并且这里使用了Spring的instanceSupplier扩展点,也即最终创建Bean对象的时候,会把创建的权利交给开发者自定义。所以最终创建的对象就是FeignClientFactortyBean对象的getObject方法。

public class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {


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

    <T> T getTarget() {
        // 从工厂中拿到FeignContext对象。
        FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
        : applicationContext.getBean(FeignContext.class);

        // 从上下文中创建出Feign.Builder对象,
        // 这里需要补充一下前置知识,Feign.Builder用于产出Feign对象,而Feign对象就是请求的上下文对象。
        Feign.Builder builder = feign(context);

        if (!StringUtils.hasText(url)) {
            …………
            // 使用Feign创建出动态代理的类
            return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
        }

        …………

        // 使用Feign创建出动态代理的类
        Targeter targeter = get(context, Targeter.class);
        return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
    }

    protected Feign.Builder feign(FeignContext context) {
        FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
        Logger logger = loggerFactory.create(type);

        // 从Spring工厂中拿到Feign组件中一个个可自定义配置的组件
        // 而我们啥时候配置过这些自定义的组件呢?
        // 没错,就是Spring boot帮我们自动配置的。
        // 所以只需要找到Spring boot帮我们自动配置的组件即可
        // 并且也能够明白,我们可以通过@Configuration+@Bean的方式自定义Feign的组件
        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));
        // @formatter:on

        configureFeign(context, builder);

        return builder;
    }
}

这里非常的简单,创建出Feign.Builder对象,此对象用于构建出Feign对象,而Feign对象作为请求的整个上下文。而我们需要把之前@FeignClient注解的燃料交给Feign,还有Spring boot帮我们自动配置的燃料也一同交给Feign,所以这里feign()方法从Spring的工厂中拿到Spring boot自动配置的组件。下面图来自Spring Cloud Openfeign中spring.factory自动配置文件自动配置的类。所以再再再再再一次证明了Spring Cloud Openfeign是承上启下。向上对接规范和扩展接口,向下对接组件本身

 

接下来,我们直接看向重点,如何动态代理和方法拦截的即可。

public abstract class Feign {
    // build方法构建出ReflectiveFeign类
    // 然后调用ReflectiveFeign类的newInstance方法
    public <T> T target(Target<T> target) {
        return build().newInstance(target);
    } 

    public Feign build() {
        super.enrich();

        SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
        new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
            responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode,
            propagationPolicy, forceDecoding);
        ParseHandlersByName handlersByName =
        new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
            errorDecoder, synchronousMethodHandlerFactory);
        return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
}


 这里把所有Feign中组件准备好,构建出ReflectiveFeign类,然后调用newInstance方法。

@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 handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
    } else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    }
}
// 创建出JDK动态代理中的拦截器对象
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动态代理中所需的拦截器对象InvocationHandler,然后调用Proxy.newProxyInstance方法创建出代理类的实例对象。所以接下来看到如何拦截并且处理的就完美的闭环了。

@Override
    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);
    }


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

// 执行,也即调用某个Http客户端的http请求
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);
        …………
    response = client.execute(request, options);
        …………
  }

最终回掉到SynchronousMethodHandler的invoke方法中,可以很清楚的看到内部就开始使用某个Http客户端,发送Http请求了。

总结:

能够理解,框架的定位,框架向上兼容什么,向下兼容什么,这对于理解源码是非常重要的一个部分。

最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员李哈

创作不易,希望能给与支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值