• Spring-Cloud-OpenFeign源码解析(上篇)


    前言

            openFeign作为一个http客户端,可以让开发人员像调用一个本地接口一样调用一个http接口,而spring cloud也将openFeign集成到了spring cloud全家桶中,成为了spring cloud体系中微服务接口调用的标准实现,同时spring cloud也扩展的openFeign,使之支持springMVC注解。feign的使用我想大部分人都已经太熟悉了,下面直接进入源码。源码版本:spring-cloud-openfeign-3.1.1

    @EnableFeignClients和@FeignClient

            源码入口就是EnableFeignClients这个注解,当开启这个注解,spring就会扫描所有打了FeignClient注解的类,并为之创建动态代理,封装为一个http客户端:

    1. @Import(FeignClientsRegistrar.class)
    2. public @interface EnableFeignClients

    可以看到EnableFeignClients引入了FeignClientsRegistrar这个类,它实现了ImportBeanDefinitionRegistrar,具有注册bean定义的功能:

    1. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    2. //注册默认的feign客户端配置类,所有的feign客户端可以公用这些配置
    3. registerDefaultConfiguration(metadata, registry);
    4. //注册feign客户端
    5. registerFeignClients(metadata, registry);
    6. }
    7. public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    8. LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
    9. Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
    10. final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
    11. //注解上的clients属性如果为空,则需要根据扫描的包去扫描带有FeignClient注解的类
    12. if (clients == null || clients.length == 0) {
    13. ClassPathScanningCandidateComponentProvider scanner = getScanner();
    14. scanner.setResourceLoader(this.resourceLoader);
    15. scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
    16. Set<String> basePackages = getBasePackages(metadata);
    17. for (String basePackage : basePackages) {
    18. candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
    19. }
    20. }
    21. else {
    22. //如果clients属性有值,则直接把指定的clients加入候选者
    23. for (Class<?> clazz : clients) {
    24. candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
    25. }
    26. }
    27. //将每个候选者注册为FeignClient
    28. for (BeanDefinition candidateComponent : candidateComponents) {
    29. if (candidateComponent instanceof AnnotatedBeanDefinition) {
    30. // verify annotated class is an interface
    31. AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
    32. AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
    33. Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
    34. Map<String, Object> attributes = annotationMetadata
    35. .getAnnotationAttributes(FeignClient.class.getCanonicalName());
    36. String name = getClientName(attributes);
    37. //注册该FeignClient的配置类,其实是注册一个FeignClientSpecification,FeignClient注解上的configuration属性
    38. //将会作为构造器参数传入。注意每个FeignClient都有自己的spring上下文。
    39. registerClientConfiguration(registry, name, attributes.get("configuration"));
    40. //注册FeignClient,其实就是注册一个FactoryBean
    41. registerFeignClient(registry, annotationMetadata, attributes);
    42. }
    43. }
    44. }
    45. private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
    46. Map<String, Object> attributes) {
    47. String className = annotationMetadata.getClassName();
    48. Class clazz = ClassUtils.resolveClassName(className, null);
    49. ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
    50. ? (ConfigurableBeanFactory) registry : null;
    51. String contextId = getContextId(beanFactory, attributes);
    52. String name = getName(attributes);
    53. //创建FeignClientFactoryBean
    54. FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
    55. factoryBean.setBeanFactory(beanFactory);
    56. factoryBean.setName(name);
    57. factoryBean.setContextId(contextId);
    58. factoryBean.setType(clazz);
    59. factoryBean.setRefreshableClient(isClientRefreshEnabled());
    60. //设置bean的instanceSupplier,指定spring bean实例化策略为supplier方式
    61. BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
    62. //下面是一些参数的赋值
    63. factoryBean.setUrl(getUrl(beanFactory, attributes));
    64. factoryBean.setPath(getPath(beanFactory, attributes));
    65. factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
    66. Object fallback = attributes.get("fallback");
    67. if (fallback != null) {
    68. factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
    69. : ClassUtils.resolveClassName(fallback.toString(), null));
    70. }
    71. Object fallbackFactory = attributes.get("fallbackFactory");
    72. if (fallbackFactory != null) {
    73. factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
    74. : ClassUtils.resolveClassName(fallbackFactory.toString(), null));
    75. }
    76. //直接使用了getObject方法拿到真正的bean
    77. return factoryBean.getObject();
    78. });
    79. //指定按类型注入
    80. definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    81. //指定懒加载
    82. definition.setLazyInit(true);
    83. validate(attributes);
    84. AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
    85. beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
    86. beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
    87. // has a default, won't be null
    88. boolean primary = (Boolean) attributes.get("primary");
    89. //指定为首选bean
    90. beanDefinition.setPrimary(primary);
    91. String[] qualifiers = getQualifiers(attributes);
    92. if (ObjectUtils.isEmpty(qualifiers)) {
    93. qualifiers = new String[] { contextId + "FeignClient" };
    94. }
    95. BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
    96. //注册bean定义
    97. BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    98. //注册可选的Request.Options,可以动态刷新Request配置,貌似不太常用
    99. registerOptionsBeanDefinition(registry, contextId);
    100. }

    这里一个最为重要的类:FeignClientFactoryBean,详细看下这个FactoryBean的getObject方法是如何创建feign客户端的:

    1. public Object getObject() {
    2. return getTarget();
    3. }
    4. <T> T getTarget() {
    5. //获取FeignContext,这其实是一个feign的上下文工厂,继承了Spring的NamedContextFactory
    6. //可以为每个feignClient创建一个独立的Spring上下文,FeignContext在FeignAutoConfiguration中自动装配
    7. FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
    8. : applicationContext.getBean(FeignContext.class);
    9. //获取一个Feign.Builder
    10. Feign.Builder builder = feign(context);
    11. if (!StringUtils.hasText(url)) {
    12. if (LOG.isInfoEnabled()) {
    13. LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
    14. }
    15. if (!name.startsWith("http")) {
    16. url = "http://" + name;
    17. }
    18. else {
    19. url = name;
    20. }
    21. url += cleanPath();
    22. //创建feign客户端
    23. return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
    24. }
    25. if (StringUtils.hasText(url) && !url.startsWith("http")) {
    26. url = "http://" + url;
    27. }
    28. String url = this.url + cleanPath();
    29. Client client = getOptional(context, Client.class);
    30. if (client != null) {
    31. if (client instanceof FeignBlockingLoadBalancerClient) {
    32. // not load balancing because we have a url,
    33. // but Spring Cloud LoadBalancer is on the classpath, so unwrap
    34. client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
    35. }
    36. if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
    37. // not load balancing because we have a url,
    38. // but Spring Cloud LoadBalancer is on the classpath, so unwrap
    39. client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
    40. }
    41. builder.client(client);
    42. }
    43. applyBuildCustomizers(context, builder);
    44. Targeter targeter = get(context, Targeter.class);
    45. return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
    46. }

    看一下创建Feign.Builder的逻辑,Feign.Builder用来创建一个Feign(ReflectiveFeign)对象,Feign对象用来为我们写的Feign接口创建动态代理:

    1. protected Feign.Builder feign(FeignContext context) {
    2. FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    3. Logger logger = loggerFactory.create(type);
    4. // @formatter:off
    5. //从feignClient上下文获取Feign.Builder,在FeignClientsConfiguration中自动装配
    6. //如果没有引入断路器或是其它扩展组件,默认就是Feign.Builder
    7. //如果引入了circuitbreaker,则为FeignCircuitBreaker.Builder
    8. Feign.Builder builder = get(context, Feign.Builder.class)
    9. // required values
    10. .logger(logger)
    11. .encoder(get(context, Encoder.class))
    12. .decoder(get(context, Decoder.class))
    13. .contract(get(context, Contract.class));
    14. // @formatter:on
    15. //根据父容器的客户端配置配置feignClient
    16. //feignClient的配置优先级,默认情况下是
    17. //FeignClientProperties>FeignClientSpecification>自定义配置类>FeignClientsConfiguration
    18. //当然也可以用feign.client.defaultToProperties设置
    19. configureFeign(context, builder);
    20. return builder;
    21. }

    然后看loadBalance(builder, context, new HardCodedTarget<>(type, name, url)),在创建feign客户端的时候会加入负载均衡器:

    1. protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
    2. //获取feign客户端,会在Feign.Builder中创建,默认是Client.Default
    3. //可以替换为像ApacheHttpClient等。
    4. //当然这里一般是FeignBlockingLoadBalancerClient,因为引入了spring cloud负载均衡器
    5. //FeignBlockingLoadBalancerClient是对Client.Default的包装,使客户端具有了负载均衡的能力
    6. Client client = getOptional(context, Client.class);
    7. if (client != null) {
    8. builder.client(client);
    9. //用FeignBuilderCustomizer配置Feign.Builder,如果有的话
    10. applyBuildCustomizers(context, builder);
    11. //从容器中获取Targeter,默认是DefaultTargeter
    12. //如果引入了断路器,则为FeignCircuitBreakerTargeter,会加入降级的逻辑fallback
    13. Targeter targeter = get(context, Targeter.class);
    14. //创建feign客户端
    15. return targeter.target(this, builder, context, target);
    16. }
    17. throw new IllegalStateException(
    18. "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
    19. }
    20. public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
    21. Target.HardCodedTarget<T> target) {
    22. if (!(feign instanceof FeignCircuitBreaker.Builder)) {
    23. return feign.target(target);
    24. }
    25. //因为引入了断路器,把Feign.Builder强转为FeignCircuitBreaker.Builder
    26. FeignCircuitBreaker.Builder builder = (FeignCircuitBreaker.Builder) feign;
    27. String name = !StringUtils.hasText(factory.getContextId()) ? factory.getName() : factory.getContextId();
    28. //获取降级类或降级工厂
    29. Class<?> fallback = factory.getFallback();
    30. if (fallback != void.class) {
    31. //以fallback class为例
    32. return targetWithFallback(name, context, target, builder, fallback);
    33. }
    34. Class<?> fallbackFactory = factory.getFallbackFactory();
    35. if (fallbackFactory != void.class) {
    36. return targetWithFallbackFactory(name, context, target, builder, fallbackFactory);
    37. }
    38. return builder(name, builder).target(target);
    39. }
    40. private <T> T targetWithFallback(String feignClientName, FeignContext context, Target.HardCodedTarget<T> target,
    41. FeignCircuitBreaker.Builder builder, Class<?> fallback) {
    42. //获取fallback对象
    43. T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
    44. //FeignCircuitBreaker.Builder#target
    45. return builder(feignClientName, builder).target(target, fallbackInstance);
    46. }

    使用Builder的build方法创建Feign对象:

    1. public Feign build(final FallbackFactory<?> nullableFallbackFactory) {
    2. //创建Feign对象(ReflectiveFeign)
    3. //设置InvocationHandler工厂为FeignCircuitBreakerInvocationHandler
    4. super.invocationHandlerFactory((target, dispatch) -> new FeignCircuitBreakerInvocationHandler(
    5. circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory,
    6. circuitBreakerGroupEnabled, circuitBreakerNameResolver));
    7. //调用父类创建ReflectiveFeign
    8. return super.build();
    9. }

    下面看一下创建ReflectiveFeign逻辑,ReflectiveFeign用来创建动态代理:

    1. public Feign build() {
    2. //在FeignClientFactoryBean#configureFeign中会把容器中的Capability设置进来
    3. //Capability对象可以增强Feign的各个组件,一般就是对原有的组件的包装
    4. Client client = Capability.enrich(this.client, capabilities);
    5. Retryer retryer = Capability.enrich(this.retryer, capabilities);
    6. List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
    7. .map(ri -> Capability.enrich(ri, capabilities))
    8. .collect(Collectors.toList());
    9. Logger logger = Capability.enrich(this.logger, capabilities);
    10. Contract contract = Capability.enrich(this.contract, capabilities);
    11. Options options = Capability.enrich(this.options, capabilities);
    12. Encoder encoder = Capability.enrich(this.encoder, capabilities);
    13. Decoder decoder = Capability.enrich(this.decoder, capabilities);
    14. InvocationHandlerFactory invocationHandlerFactory =
    15. Capability.enrich(this.invocationHandlerFactory, capabilities);
    16. QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
    17. //SynchronousMethodHandler.Factory,用来创建MethodHandler的工厂
    18. SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
    19. new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
    20. logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
    21. //ParseHandlersByName,用来创建方法名与对应MethodHandler的映射关系
    22. ParseHandlersByName handlersByName =
    23. new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
    24. errorDecoder, synchronousMethodHandlerFactory);
    25. return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    26. }
    27. }

    Feign#newInstance创建feign接口的动态代理:

    1. public <T> T newInstance(Target<T> target) {
    2. //这里的target为HardCodedTarget,包含了该feign客户端的接口类型,name,url
    3. //用ParseHandlersByName创建方法名和MethodHandler的映射map
    4. Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    5. //Method对象和MethodHandler的映射map
    6. Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    7. List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    8. //遍历接口的每个方法
    9. for (Method method : target.type().getMethods()) {
    10. if (method.getDeclaringClass() == Object.class) {
    11. continue;
    12. } else if (Util.isDefault(method)) {
    13. //如果是default方法,则用DefaultMethodHandler处理,这种方法会使用java7的MethodHandle调用
    14. DefaultMethodHandler handler = new DefaultMethodHandler(method);
    15. defaultMethodHandlers.add(handler);
    16. methodToHandler.put(method, handler);
    17. } else {
    18. //如果是自定义的方法,也就是我们自己写的方法则从nameToHandler里面获取对应的SynchronousMethodHandler
    19. methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
    20. }
    21. }
    22. //jdk动态代理的InvocationHandler,这里为FeignCircuitBreakerInvocationHandler
    23. InvocationHandler handler = factory.create(target, methodToHandler);
    24. //创建动态代理
    25. T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
    26. new Class<?>[] {target.type()}, handler);
    27. for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    28. //将MethodHandle绑定到代理对象
    29. defaultMethodHandler.bindTo(proxy);
    30. }
    31. return proxy;
    32. }

    动态代理这下就创建完了,接下来是feign接口的调用过程。

    未完待续。。。

  • 相关阅读:
    Docker:容器网络互联
    Ctfshow web入门 XSS篇 web316-web333 详细题解 全
    Python **运算符(python**kwargs:参数解包)(kwargs:keyword arguments)
    Java解析生成二维码
    嵌入式启动细节
    WordPress 上传图片时自动重命名的方法
    《Linux运维总结:使用elasticdump工具迁移单节点elasticsearch数据(方案一)》
    【英雄哥六月集训】第 22天: 有序集合
    pat考完了
    java技术专家面试指南50问【java学习+面试宝典】(六)
  • 原文地址:https://blog.csdn.net/w7sss/article/details/125600814