2023OpenFeign源码

原理-源码

让我们看着源~码~ 按顺序走~趟流~程~
分为两大部分:启动原理、调用流程
Feign 的Java 代码才 3w 多行,放眼现在热门的开源项目, Dubbo、Naocs、Skywalking 中 Java 代码都要 30w 行起步。

重要通知!看源码,就上新东方 一定要找对地方!!!
在一个SpringCloud项目中,我们引入了spring-cloud-openfeign依赖,而该依赖又引入了openfeign-core的依赖;
他们分别对应的github代码仓库分别是spring-cloud-openfeign和OpenFeign/feign

  • spring-cloud-openfeign是openFeign和SpringCloud的结合,我们的@EnableFeignClients注解、服务启动时的初始化工作就在此项目中

  • openfeign-core是OpenFeign/feign仓库的core模块,该仓库是feign的“娘家”,单独作为远程调用组件而存在的feign,没有“嫁入Spring Cloud豪门”(未被整合进Spring Cloud成套解决方案的单独组件,若单独使用,则启动流程略有不同)

    • 比如不会随启动自动注册Spring Bean,没有我们刚学的一些注解,
    • 仓库内还包含了不需要学习的测试模块、备选的http库实现等。

比如启动流程,在注册、代理Bean的过程中,会先从SpringCloud OpenFeign的源码看起,而其中引入的Feign包下的源码,则需要转到OpenFeign/feign仓库;
本文为了方便大家学习,会将他们整合到一起,需要跨仓库的地方会提一嘴,做到梳理流程无痛衔接。毕竟我们学习目的是流程原理,而不是为了学某一具体仓库。

依赖

总项目

			<dependency>
				<groupId>org.springframework.cloudgroupId>
				<artifactId>spring-cloud-openfeign-dependenciesartifactId>
				<version>${project.version}version>
				<type>pomtype>
				<scope>importscope>
			dependency>

core模块

		<dependency>
			<groupId>io.github.openfeigngroupId>
			<artifactId>feign-coreartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>io.github.openfeign.formgroupId>
			<artifactId>feign-form-springartifactId>
			<exclusions>

				<exclusion>
					<groupId>commons-iogroupId>
					<artifactId>commons-ioartifactId>
				exclusion>
				<exclusion>
					<groupId>commons-fileuploadgroupId>
					<artifactId>commons-fileuploadartifactId>
				exclusion>
			exclusions>
		dependency>

		<dependency>
			<groupId>io.github.openfeigngroupId>
			<artifactId>feign-slf4jartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>io.github.openfeigngroupId>
			<artifactId>feign-micrometerartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>io.github.openfeigngroupId>
			<artifactId>feign-hc5artifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>io.github.openfeigngroupId>
			<artifactId>feign-okhttpartifactId>
			<optional>trueoptional>
		dependency>
		<dependency>
			<groupId>io.github.openfeigngroupId>
			<artifactId>feign-java11artifactId>
			<optional>trueoptional>
		dependency>

辞典

contextId: FeignClient的容器id,通过该id从Spring容器中获取Bean;

代码风格

Builder\Factory都定义在自己内部。看到XXX.Builder,就知道是用来创建XXX的。
几乎所有获取对象的方法,都是用target()方法,内部build().newInstance()

OpenFeign,启动!(原理)

服务启动阶段,我们需要读取配置、注册Bean;
需要注册的Bean组件有:

启动时factories

org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration
org.springframework.cloud.openfeign.FeignAutoConfiguration
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration
  • 这个和注解扫描谁先谁后?
  • 注解是怎么扫描的?

入口类注解@EnableFeignClients

@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

    Class<?>[] defaultConfiguration() default {};

    Class<?>[] clients() default {};
}

@Import 常用于动态注册Spring Bean
2023OpenFeign源码_第1张图片
FeignClientsRegistrar.class实现ResourceLoaderAware、EnvironmentAware接口, 重写Set方法为 FeignClientsRegistrar 中两个属性 resourceLoader、environment 赋值,

  • can can spring

ImportBeanDefinitionRegistrar 负责动态注入 IOC Bean,分别注入 Feign 配置类、FeignClient Bean
FeignClientsRegistrar源码

// 资源加载器,可以加载 classpath 下的所有文件
private ResourceLoader resourceLoader;
// 上下文,可通过该环境获取当前应用配置属性等
private Environment environment;
//..
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   // 注册 @EnableFeignClients 提供的自定义配置类中的相关 Bean 实例
    registerDefaultConfiguration(metadata,registry);
    // 扫描 packge,注册被 @FeignClient 修饰的接口类为 IOC Bean
    registerFeignClients(metadata, registry);
}
  • 为什么有了factory还要有factoryBean?

注册 FeignClient 接口

registerFeignClients方法处理带有 @FeignClient 的接口

1.扫描 @EnableFeignClients 注解,如果有在clients属性上配置client,则加载指定接口;若没有配置,则用 scanner在bashPackage下扫描出被@FeignClient 修饰的接口
2.获取 @FeignClient 上的属性,根据 configuration 属性去创建接口级的 FeignClientSpecification 配置类 IOC Bean
3.将 @FeignClient 注解上的属性设置到 FeignClientFactoryBean 对象上,并注册 IOC Bean

@FeignClient 修饰的接口使用了 Spring 的代理工厂生成代理类,所以这里会把修饰了 @FeignClient 接口的 BeanDefinition 设置为 FeignClientFactoryBean 类型,而 FeignClientFactoryBean 继承自 FactoryBean

根据Spring加载Bean的过程,当我们用@FeignClient 修饰interface时,注册到 IOC 容器中 Bean 类型其实是其工厂 FeignClientFactoryBean;当我们要获取这个bean时,再调用工厂的getObjtct()生产出bean;

之前我们手写工厂模式的案例的时候,在创建对象时经常是亲自调用Factory的创建方法;
但在Spring中,(作为中间层的容器层?)Spring统一包揽了Bean对象的创建和管理。我们可以只要无脑getbean,而具体细节,如是先用工厂创建还是直接返回之前的Bean ,直接由中间的Spring管理了,我们只要无脑get。甚至在注解方式下,Get都省了,直接@Autowired。

  • 这里既有代理(?)又有工厂的思想。代理下,我们只管找中间层要bean这一“产品”,别管这东西哪儿来的,甚至别管有没有生产出来,这让代理想办法;
    • 而代理如果缓存里有Bean就直接返回,没有的话他自己再找工厂要。这种具体的小事不要烦调用它的程序猿。
    • 突然有种当领导的感觉,哈哈。大决战的林总。
  • 如果说工厂模式解决了创建对象不需要知道具体对象细节的问题,那么工厂+代理就一条龙解决了不用知道工厂细节、管理细节的问题,哈哈。

在 Spring 中,FactoryBean 是一个工厂 Bean,用来创建代理 Bean。/对于需要获取 Bean 的消费者而言,它是不知道 Bean 是普通 Bean 或是工厂 Bean 的。工厂 Bean 返回的实例不是工厂 Bean 本身,而是会返回执行了工厂 Bean 中 FactoryBean#getObject 逻辑的实例

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet();
// if @EnableFeignClients参数中有name属性的情况,此处暂且省略
 else {
            ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
            scanner.setResourceLoader(this.resourceLoader);
            scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
            Set<String> basePackages = this.getBasePackages(metadata);
            Iterator var8 = basePackages.iterator();

            while(var8.hasNext()) {
                String basePackage = (String)var8.next();
                candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
            }
        }

        Iterator var13 = candidateComponents.iterator();

        while(var13.hasNext()) {
            BeanDefinition candidateComponent = (BeanDefinition)var13.next();
            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 = this.getClientName(attributes);
                this.registerClientConfiguration(registry, name, attributes.get("configuration"));
                this.registerFeignClient(registry, annotationMetadata, attributes);
            }
        }

    }

    private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        Class clazz = ClassUtils.resolveClassName(className, (ClassLoader)null);
        ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory)registry : null;
        String contextId = this.getContextId(beanFactory, attributes);
        String name = this.getName(attributes);
        FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
        factoryBean.setBeanFactory(beanFactory);
        factoryBean.setName(name);
        factoryBean.setContextId(contextId);
        factoryBean.setType(clazz);
        factoryBean.setRefreshableClient(this.isClientRefreshEnabled());
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
            factoryBean.setUrl(this.getUrl(beanFactory, attributes));
            factoryBean.setPath(this.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(), (ClassLoader)null));
            }

            Object fallbackFactory = attributes.get("fallbackFactory");
            if (fallbackFactory != null) {
                factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class)fallbackFactory : ClassUtils.resolveClassName(fallbackFactory.toString(), (ClassLoader)null));
            }

            return factoryBean.getObject();
        });
        definition.setAutowireMode(2);
        definition.setLazyInit(true);
        this.validate(attributes);
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        beanDefinition.setAttribute("factoryBeanObjectType", className);
        beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
        boolean primary = (Boolean)attributes.get("primary");
        beanDefinition.setPrimary(primary);
        String[] qualifiers = this.getQualifiers(attributes);
        if (ObjectUtils.isEmpty(qualifiers)) {
            qualifiers = new String[]{contextId + "FeignClient"};
        }

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
        this.registerOptionsBeanDefinition(registry, contextId);
    }

    }

相关类

 protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent() && !beanDefinition.getMetadata().isAnnotation()) {
                    isCandidate = true;
                }

                return isCandidate;
            }
        };
    }

    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
        Set<String> basePackages = new HashSet();
        String[] var4 = (String[])((String[])attributes.get("value"));
        int var5 = var4.length;

        int var6;
        String pkg;
        for(var6 = 0; var6 < var5; ++var6) {
            pkg = var4[var6];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        var4 = (String[])((String[])attributes.get("basePackages"));
        var5 = var4.length;

        for(var6 = 0; var6 < var5; ++var6) {
            pkg = var4[var6];
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }

        Class[] var8 = (Class[])((Class[])attributes.get("basePackageClasses"));
        var5 = var8.length;

        for(var6 = 0; var6 < var5; ++var6) {
            Class<?> clazz = var8[var6];
            basePackages.add(ClassUtils.getPackageName(clazz));
        }

        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }

        return basePackages;
    }

2023OpenFeign源码_第2张图片
依据 Spring InitializingBean 接口:FeignClientFactoryBean在类初始化时执行一段逻辑;
依据 Spring FactoryBean 接口:如果它被别的类 @Autowired 进行注入,返回的不是它本身,而是 FactoryBean#getObject 返回的类,
依据 Spring ApplicationContextAware 接口:它能够获取 Spring 上下文对象,赋值到对象的局部变量里。

  • Resource怎么样?

初始化逻辑没有特别的操作,只是使用断言工具类判断两个字段不为空。

@Override
public void afterPropertiesSet() {
    Assert.hasText(contextId, "Context id must be set");
    Assert.hasText(name, "Name must be set");
}

关键看 FactoryBean#getObject 方法

@Override
public Object getObject() throws Exception {
    return getTarget();
}
  • Target是什么?

getTarget() 源码挺长的,这里分段展示:

这里提出一个疑问:FeignContext 什么时候、在哪里被注入到 Spring 容器里的?![请添加图片描述](https://img-blog.csdnimg.cn/37ff2926b178428c8e0af5873ed66bbe.bmp)
使用了 SpringBoot 自动装配的功能,FeignContext 就是在 FeignAutoConfiguration 中被成功创建
初始化父子容器
这里涉及到 Spring 父子容器的概念,默认子容器 Map 为空,获取不到服务名对应 Context 则新建

从下图中看到,注册了一个 FeignClientsConfiguration 类型的 Bean,我们上述方法 feign 中的获取的编码、解码器等组件都是从此类中获取默认

默认注册如下,FeignClientsConfiguration 是由创建 FeignContext 调用父类 Super 构造方法传入的

关于父子类容器对应关系,以及提供 @FeignClient 服务对应子容器的关系(每一个服务对应一个子容器实例)

回到 getInstance 方法,子容器此时已加载对应 Bean,直接通过 getBean 获取 FeignLoggerFactory

如法炮制,Feign.Builder、Encoder、Decoder、Contract 都可以通过子容器获取对应 Bean

configureFeign 方法主要进行一些配置赋值,比如超时、重试、404 配置等,就不再细说赋值代码了

到这里有必要总结一下创建 Spring 代理工厂的前半场代码

  1. 注入@FeignClient 服务时,调用 FactoryBean#getObject 返回的代理对象
  2. 通过 IOC 容器获取 FeignContext 上下文
  3. 创建 Feign.Builder 对象时会创建 Feign 服务对应的子容器
  4. 从子容器中获取日志工厂、编码器、解码器等 Bean
  5. 为 Feign.Builder 设置配置,比如超时时间、日志级别等属性,每一个服务都可以个性化设置

用applicationContext.getBean()获取FeignContext,然后用feign(context)获取Builder;
然后先以FeignContext为参数创建Client对象,再用Builder构建Client。

又一个Builder案例

动态代理生成Bean

继续看FeignClientFactory#getTarget方法:
此时的Targeter是HystrixTargeter的对象,Client则是LoadBalanceFeignClient

新版不是Hystrix了,而是自己的FeignCircuitBreakerTargeter.java

	<T> T getTarget() {
		FeignClientFactory feignClientFactory = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class)
				: applicationContext.getBean(FeignClientFactory.class);
		Feign.Builder builder = feign(feignClientFactory);
		//if分支:@注解上没有url的话,则使用负载均衡的Client
		if (!StringUtils.hasText(url) && !isUrlAvailableInConfig(contextId)) {

			//...log
			if (!name.startsWith("http")) {
				url = "http://" + name;
			}
			else {
				url = name;
			}
			url += cleanPath();
			return (T) loadBalance(builder, feignClientFactory, new HardCodedTarget<>(type, name, url));
		}
		if (StringUtils.hasText(url) && !url.startsWith("http")) {
			url = "http://" + url;
		}
		String url = this.url + cleanPath();
		Client client = getOptional(feignClientFactory, Client.class);
		if (client != null) {
			if (client instanceof FeignBlockingLoadBalancerClient) {
				// not load balancing because we have a url,
				// but Spring Cloud LoadBalancer is on the classpath, so unwrap
				client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
			}
			if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
				// not load balancing because we have a url,
				// but Spring Cloud LoadBalancer is on the classpath, so unwrap
				client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
			}
			builder.client(client);
		}

		applyBuildCustomizers(feignClientFactory, builder);

		Targeter targeter = get(feignClientFactory, Targeter.class);
		return targeter.target(this, builder, feignClientFactory, resolveTarget(feignClientFactory, contextId, url));
	}

targeter: 一个接口,用target方法返回一个对象(此处为FeignClient的bean);
负载均衡的targeter直接从FeignClientFactory context根据HardCodedTarget类获取。需要引入spring-cloud-starter-loadbalancer模块,可能是在那里放入上下文的吧。
最终的targeter.target()返回的是Feign.Builder的target方法返回的对象;
而Feign.Builder又是feign(feignClientFactory)返回的,是用get(FeignClientFactory context, Feign.Builder.class)从上下文获取的

  • import feign.Target.HardCodedTarget;哪里的代码?没见feign,是依赖里的么
  • Feign.Builder这个包没找到,倒是FeignClientBuilder.java里有个内部类Builder,但应该不是这个
    • 是feign仓库里的。但是FeignClientBuilder已经是个builder了还要内部Builder?

这下不得不看get方法了:

	protected <T> T get(FeignClientFactory context, Class<T> type) {
		T instance = context.getInstance(contextId, type);
		if (instance == null) {
			throw new IllegalStateException("也妹(没)找着这个bean呐!");
		}
		return instance;
	}

看源码怕太枯燥了,报错信息我就整活翻译了下hhh

  • contextid那儿来的?
    factory为啥是context?

FeignClientFactory的getInstance方法:

	public <T> T getInstance(String contextName, String beanName, Class<T> type) {
		return getContext(contextName).getBean(beanName, type);
	}

getContext可能是父类NamedContextFactory 的方法,这里can can spring。
终于是返回了Builder了。

Client: Feign 发送请求以及接收响应等都是由 Client 完成,该类默认 Client.Default,另外支持 HttpClient、OkHttp 等客户端

代码中的 Client、Targeter 在自动装配时注册,配合上文中的父子容器理论,这两个 Bean 在父容器中存在

为了使用负载均衡,我们常在 @FeignClient 注解是使用 服务name 而不是 url。因为如源码所示,不传入url才会执行负载均衡策略的分支, return一个loadBalance()返回的targeter.target()。

  • 注入这个?那Client哪里去了?

Feign依赖的spring-cloud-loadbalancer,

获取到Builder和Targeter之后,我们将Builder传给Targeter的getTarget(),获取我们想要的FeignClient的代理类

  • 怎么知道是FeignCircuitBreakerTargeter? 自动装备时注册的?
  • 那怎么样又让他不是呢?

查看FeignCircuitBreakerTargeter的 target()方法

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignClientFactory context,
			Target.HardCodedTarget<T> target) {
		if (!(feign instanceof FeignCircuitBreaker.Builder builder)) {
			return feign.target(target);
		}
		String name = !StringUtils.hasText(factory.getContextId()) ? factory.getName() : factory.getContextId();
		Class<?> fallback = factory.getFallback();
		if (fallback != void.class) {
			return targetWithFallback(name, context, target, builder, fallback);
		}
		Class<?> fallbackFactory = factory.getFallbackFactory();
		if (fallbackFactory != void.class) {
			return targetWithFallbackFactory(name, context, target, builder, fallbackFactory);
		}
		return builder(name, builder).target(target);
	}

判断传入的Builder是FeignCircuitBreaker.Builder对象,调用其target(target)方法,该方法返回build(null).newInstance(target)。

		@Override
		public <T> T target(Target<T> target) {
			return build(null).newInstance(target);
		}

		public Feign build(final FallbackFactory<?> nullableFallbackFactory) {
			super.invocationHandlerFactory((target, dispatch) -> new FeignCircuitBreakerInvocationHandler(
					circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory,
					circuitBreakerGroupEnabled, circuitBreakerNameResolver));
			return super.build();
		}

其父类是Feign.Builder。这里是先设置invocationHandlerFactory,再调用父类的build方法:
feign/core/src/main/java/feign/Feign.java

    public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }

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

      final ResponseHandler responseHandler =
          new ResponseHandler(logLevel, logger, decoder, errorDecoder,
              dismiss404, closeAfterDecode, responseInterceptor);
      MethodHandler.Factory<Object> methodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
              responseHandler, logger, logLevel, propagationPolicy,
              new RequestTemplateFactoryResolver(encoder, queryMapEncoder),
              options);
      return new ReflectiveFeign<>(contract, methodHandlerFactory, invocationHandlerFactory,
          () -> null);
    }
  }

好家伙,不但自己内部定义factory接口,build也是定义到自己内部,自己建造自己了属于是

Feign.bulild()创建反射类 ReflectiveFeign的对象,该类设置编码、解码、重试等属性;

  • Feign对象到底是干什么的?返回他干嘛
    • 怎么设置的? 和Dubbo对比如何?

ReflectiveFeign的 newInstance(target) 方法对 @FeignClient 修饰的接口中 SpringMvc 等配置进行解析转换,对接口类中的方法进行归类,生成动态代理类
newInstance()按照行为大致划分,共做了四件事

  1. 处理 @FeignClient 注解(SpringMvc 注解等)封装为 MethodHandler 包装类
  2. 遍历接口中所有方法,过滤 Object 方法,并将默认方法以及 FeignClient 方法分类 为啥要过滤?
  3. 创建动态代理对应的 InvocationHandler 并创建 Proxy 实例
  4. 接口内 default 方法 绑定动态代理类 为啥是他们

feign/core/src/main/java/feign/ReflectiveFeign.java

  /**
   * creates an api binding to the {@code target}. As this invokes reflection, care should be taken
   * to cache the result.
   */
  public <T> T newInstance(Target<T> target) {
    return newInstance(target, defaultContextSupplier.newContext());
  }

  @SuppressWarnings("unchecked")
  public <T> T newInstance(Target<T> target, C requestContext) {
    TargetSpecificationVerifier.verify(target);

    Map<Method, MethodHandler> methodToHandler =
        targetToHandlersByName.apply(target, requestContext);
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (MethodHandler methodHandler : methodToHandler.values()) {
      if (methodHandler instanceof DefaultMethodHandler) {
        ((DefaultMethodHandler) methodHandler).bindTo(proxy);
      }
    }

使用参数传来的invocationFactory创建InvocationHandler。在该案例中,传来的Factory是cloud中的new FeignCircuitBreakerInvocationHandler。

看来cloud OpenFeign和feign的不同,这里是一处

  • InvocationHandler和methodhandler什么区别?说起来我连这个类干什么的都不知道。proxy怎么创建的,这种handler反射怎么实现的,也有必要严查!!
  • 从上文代码来看DefaultMethodHandler才进行最后的绑定,那要是不是Default的呢?方法怎么代理?

MethodHandler 将方法参数、方法返回值、参数集合、请求类型、请求路径进行解析存储

  • 似乎是因为Proxy.newInstance()方法需要handler
  • 返回的是泛型T类型
  • mvc的什么配置?
  • 大概知道为什么自己看不懂了,,一堆target,给target()传入target参数来返回target,,这Tm谁写的代码

可以看出 Feign 创建动态代理类的方式和 Mybatis Mapper 处理方式是一致的,因为两者都没有实现类

  • mapper咋创建的来着,细节也一样吗?

创建MethodHandler

feign/core/src/main/java/feign/ReflectiveFeign.java自己内部一通瞎jb调用后:

    private MethodHandler createMethodHandler(final Target<?> target,
                                              final MethodMetadata md,
                                              final C requestContext) {
      if (md.isIgnored()) {
        return args -> {
          throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
        };
      }

      return factory.create(target, md, requestContext);
    }
  }

没错这个MethodHandler的Factory还是定义在他自己内部的。至于实现,是创建反射Feign代理对象的时候new出来的SynchronousMethodHandler,刚才已经看到源码了
我以为这个叫createXX的方法要自己亲自创建methon了,没想到还是调用内部工厂类
接口自己里面定义自己的工厂接口,实现的时候也是实现类+内部实现接口,哈哈。内部接口语法上要求内部实现吗?

原理总结:
@EnableFeignClient扫描带有@FeignClient注解的接口,生成动态代理类,并为接口内的方法绑定methodHandler;(注册成Bean?)我们用@Autowired注入的bean其实就是自动生成的代理类,按Spring的正常流程,调用FactoryBean的getObject()方法生成。

  • 为什么不直接代理或者重写,而是用Handler?类也用Handler,方法也handler
  • 什么时候扫描的?怎么扫描?
  • 什么时候具体生成这个类?

也就是说在我们调用 @FeignClient接口时,会被 FeignInvocationHandler#invoke 拦截,并在动态代理方法中进行远程调用

  • 拦截?应当说注入的是代理类吧。还是说被Handler拦截了?

调用流程

  • 具体的还是要先看看Ribbon

  • methodHandler到底是feign中的还是cloud中的那个?

    • 应该是feign中的。cloud的invocationHandler不是处理方法的。
    • 也许要看看spring AOP动态代理原理是否相同。因为handler反向代理的方式没自己的反射中见过。
  • import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;

FeignCircuitBreakerInvocationHandler.java

 @Override
	public Object invoke(final Object proxy, final Method method, final Object[] args) {
		// early exit if the invoked method is from java.lang.Object
		// code is the same as ReflectiveFeign.FeignInvocationHandler
		if ("equals".equals(method.getName())) {
			//...对equals\hashcode等Object类的方法,不用代理
		}

		String circuitName = circuitBreakerNameResolver.resolveCircuitBreakerName(feignClientName, target, method);
		CircuitBreaker circuitBreaker = circuitBreakerGroupEnabled ? factory.create(circuitName, feignClientName)
				: factory.create(circuitName);
		Supplier<Object> supplier = asSupplier(method, args);
		if (this.nullableFallbackFactory != null) {
			Function<Throwable, Object> fallbackFunction = throwable -> {
				Object fallback = this.nullableFallbackFactory.create(throwable);
				try {
					return this.fallbackMethodMap.get(method).invoke(fallback, args);
				}
				catch (Exception exception) {
					unwrapAndRethrow(exception);
				}
				return null;
			};
			return circuitBreaker.run(supplier, fallbackFunction);
		}
		return circuitBreaker.run(supplier);
	}
  1. 接口注解信息封装为 HTTP Request
  2. 通过 Ribbon 获取服务列表,并对服务列表进行负载均衡调用(服务名转换为 ip+port)
  3. 请求调用后,将返回的数据封装为 HTTP Response,继而转换为接口中的返回类型

feign/

 @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;
      }
    }
  }
  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);
	// ...log

    Response response;
    long start = System.nanoTime();
    try {
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 12
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
		// ... log and throw 
    }

    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    return responseHandler.handleResponse(
        metadata.configKey(), response, metadata.returnType(), elapsedTime);
  }
  • methodHandler是私有构造方法,是单例模式吗?

封装Request

如上文MethodHandler的invoke()所示:
先创建requestTemplate:调用传入的RequestTemplate.Factory这一内部接口的create(Object[] args)方法创建。

还是第一次见到把工厂类接口定义为自己内部接口的工厂模式实现,可能是写着方便吧。
create()方法调用resolve()方法解析参数、返回template实例
RequestTemplate.Factory接口在RequestTemplateFactoryResolver.java文件中有静态实现类BuildTemplateByResolvingArgs,该类又有两个静态子类,根据参数情况分别调用这三个类的resolve方法。

  • 回头研究下springboot的restTemplate怎么做的

将RequestTemplate对象转换成Request对象:在template上aplly拦截器,然后target.applly()即可。

Client.execute()执行请求

  • client实例从哪里来的?
    • 没看到SynchronousMethodHandler什么时候初始化的,所以不知道Client,,甚至构造方法也是私有的
      • 看到了,,是Feign.java创建反射Feign的时候new出的Factory,在反射Feign里,
      • Feign. builder定义的时候有个private Client client = new Client.Default(null, null);不知道有其他初始化没有
      • 好吧早在FeignClientFactory#getTarget里设置了client,,
        直接从上下文getContext(name).getBeansOfType(type);了

DefaultLoadBalancerClientConfiguration中用了FeignBlockingLoadBalancerClient;

  • 你怎么知道是这个配置类的算话?
  • 怎么阻塞的?

FeignBlockingLoadBalancerClient:

@Override
	public Response execute(Request request, Request.Options options) throws IOException {
		final URI originalUri = URI.create(request.url());
		String serviceId = originalUri.getHost();
		Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
		String hint = getHint(serviceId);
		DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(
				new RequestDataContext(buildRequestData(request), hint));
		Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
				.getSupportedLifecycleProcessors(
						loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
						RequestDataContext.class, ResponseData.class, ServiceInstance.class);
		supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
		ServiceInstance instance = loadBalancerClient.choose(serviceId, lbRequest);
		org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
				instance);
		if (instance == null) {
			String message = "Load balancer does not contain an instance for the service " + serviceId;
			//...服务不可用,自行封装response返回				
		}
		String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();
		Request newRequest = buildRequest(request, reconstructedUrl, instance);
		return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,
				supportedLifecycleProcessors);
	}

选择服务

从loadBalancerClient.choose(serviceId, lbRequest);开始,属于spring-cloud-loadbalancer模块
choose()方法是LoadBalancer接口的父接口ServiceInstanceChooser的方法(好家伙接口之间的extends,,不过也是,你总不能implments)

好吧,下一阶段目标:看看自动装配。
话说为什么build模式都不喜欢用setXXX方式命名,,有个http库,,还XXXBuilder.create(),,,谁知道你create()的是目标类还是Builder,,,

执行远端调用逻辑中使用到了 Rxjava (响应式编程),可以看到通过底层获取 server 后将服务名称转变为 ip+port 的方式

这种响应式编程的方式在 SpringCloud 中很常见,Hystrix 源码底层也有使用
网络调用默认使用 HttpURLConnection
调用远端服务后,再将返回值解析正常返回,到这里一个完整的 Feign 调用链就结束了。

补充

启动时,使用了建造者模式

    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());
    }

feign的InvacationHandler源码真是让人难绷

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  /**
   * Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a
   * single method.
   */
  interface MethodHandler {

    Object invoke(Object[] argv) throws Throwable;

    interface Factory<C> {
      MethodHandler create(Target<?> target,
                           MethodMetadata md,
                           C requestContext);
    }
  }

  static final class Default implements InvocationHandlerFactory {

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

一个接口内部定义一个接口,再在内部接口内定义自己的工厂
底下有个内部类直接命名为Default,跟关键字default只差一个大小写

参考资料:
https://zhuanlan.zhihu.com/p/346273428

你可能感兴趣的:(微服务,java,spring,cloud)