Feign 源碼分析

Feign 源碼分析

Feign內部實現原理是基於restTemplate,而restTemplate是基於httpClient,他可以整ribbon、hystric etc 在springcloud使用篇我做了詳細的介紹

源碼分析

開啓註解掃描,分析他是如何與spring整合

EnableFeignClients({“xxx.xxxx.xxx”})

FeignClientsRegistrar 導入該類到spring容器中。前面我們在spring源碼分析中,已經對Import進行源碼分析。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)

在分析FeignClientsRegistrar 該類之前我們先分析以下幾個類

  • ImportBeanDefinitionRegistrar 在導入該類的時候初始化完成的時候回回調該方法registerBeanDefinitions 這也是spring擴展的地方

  • ResourceLoaderAware 設置當前上下文的加載器

  • EnvironmentAware 環境加載

org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions

@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		// 註冊默認配置
		registerDefaultConfiguration(metadata, registry);
		// 掃描所有feignClient註解
		registerFeignClients(metadata, registry);
	}

org.springframework.cloud.openfeign.FeignClientsRegistrar#registerDefaultConfiguration

private void registerDefaultConfiguration(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
			// 通feign的名字獲取到所有使用EnableFeignClients 的註解
		Map<String, Object> defaultAttrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
		if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
			String name;
			if (metadata.hasEnclosingClass()) {
				name = "default." + metadata.getEnclosingClassName();
			}
			else {
				name = "default." + metadata.getClassName();
			}
			registerClientConfiguration(registry, name,
					defaultAttrs.get("defaultConfiguration"));
		}
	}

org.springframework.cloud.openfeign.FeignClientsRegistrar#registerClientConfiguration 通過下面類註冊到spring容器中,接續分析

private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
			Object configuration) {
			// 加載FeignClientSpecification bean
			// 構建builer 
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientSpecification.class);
				// 設置到構造函數
		builder.addConstructorArgValue(name);
		//  添加構造參數
		builder.addConstructorArgValue(configuration);
		// 註冊bean
		registry.registerBeanDefinition(
				name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}

org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients

	public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		// 加載ClassPath下面的類
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);
		Set<String> basePackages;
		// 獲取到EnableFeignClients的屬性
		Map<String, Object> attrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName());
		// 掃描帶有FeignClient註解的類
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
				FeignClient.class);
		//獲取@EnableFeignClients 中clients的值
		final Class<?>[] clients = attrs == null ? null
				: (Class<?>[]) attrs.get("clients");
		if (clients == null || clients.length == 0) {
			//如果沒有設置,那麼加入要掃描的註解和掃描的包
			scanner.addIncludeFilter(annotationTypeFilter);
			// 確定掃描的包路徑列表
			basePackages = getBasePackages(metadata);
		}
		else {
			//如果設置了,最終掃出來的Bean必須是註解中設置的那些
			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)));
		}
		//循環掃描,並把根據註解信息,進行相關注冊
		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();
					//必須註解在interface上
					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"));

					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}

org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClient

private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
		validate(attributes);
		//將屬性設置到FeignClientFactoryBean 中
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		String name = getName(attributes);
		definition.addPropertyValue("name", name);
		definition.addPropertyValue("type", className);
		definition.addPropertyValue("decode404", attributes.get("decode404"));
		definition.addPropertyValue("fallback", attributes.get("fallback"));
		definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
		//設置Autowire 類型
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

		String alias = name + "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;
		}
		//註冊bean
		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
				new String[] { alias });
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

另一個往Registry裏面添加的BeanDefinition則是FeignClientFactoryBean,負責註冊FeignClient.
也就是說,feign的註冊一共分爲以下幾步。

FeignClientFactoryBean,他是一個工廠類,Spring Context創建bean會調用getObject方法

org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject

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

org.springframework.cloud.openfeign.FeignClientFactoryBean#getTarget

<T> T getTarget() {
		FeignContext context = applicationContext.getBean(FeignContext.class);
		//獲取一個默認的builder
		Feign.Builder builder = feign(context);

		if (!StringUtils.hasText(this.url)) {
			//如果沒有指定url,獲取name值拼接默認url
			String url;
			if (!this.name.startsWith("http")) {
				url = "http://" + this.name;
			}
			else {
				url = this.name;
			}
			url += cleanPath();
			return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
					this.name, 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) {
				//使用ribbon提供的負載均衡
				// 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));
	}

總結: 如果指定URL,沒有指定Client就使用URL,相當於直連,沒有負載均衡。指定了client,可以使用負載均衡。默認版本開啓負載均衡。

org.springframework.cloud.openfeign.FeignClientFactoryBean#feign

	protected Feign.Builder feign(FeignContext context) {
		//獲取FeignClientsConfiguration 中註冊的bean ,設置到feign中
		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
		Logger logger = loggerFactory.create(this.type);


		// @formatter:off
		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;
	}

編碼器、解碼器等等在哪裏初始化呢》。。。。 等等。

@Bean
	@ConditionalOnMissingBean
	public Decoder feignDecoder() {
		return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
	}

	@Bean
	@ConditionalOnMissingBean
	public Encoder feignEncoder() {
		return new SpringEncoder(this.messageConverters);
	}

org.springframework.cloud.openfeign.FeignClientFactoryBean#configureFeign

	protected void configureFeign(FeignContext context, Feign.Builder builder) {
		//獲取.properties的屬性
		FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
		if (properties != null) {
			if (properties.isDefaultToProperties()) {
				//默認爲true
				configureUsingConfiguration(context, builder);
				configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
				configureUsingProperties(properties.getConfig().get(this.name), builder);
			} else {
				configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
				configureUsingProperties(properties.getConfig().get(this.name), builder);
				configureUsingConfiguration(context, builder);
			}
		} else {
			configureUsingConfiguration(context, builder);
		}
	}

用戶自定義
org.springframework.cloud.openfeign.FeignClientFactoryBean#configureUsingConfiguration

//獲取用戶通過configuration @Bean的自定義配置
	protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
		Logger.Level level = getOptional(context, Logger.Level.class);
		if (level != null) {
			builder.logLevel(level);
		}
		Retryer retryer = getOptional(context, Retryer.class);
		if (retryer != null) {
			builder.retryer(retryer);
		}
		ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
		if (errorDecoder != null) {
			builder.errorDecoder(errorDecoder);
		}
		//connectTimeoutMillis和readTimeoutMillis的默認值
		Request.Options options = getOptional(context, Request.Options.class);
		if (options != null) {
			builder.options(options);
		}
		Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
				this.name, RequestInterceptor.class);
		if (requestInterceptors != null) {
			builder.requestInterceptors(requestInterceptors.values());
		}

		if (decode404) {
			builder.decode404();
		}
	}

配置文件讀取

protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) {
		if (config == null) {
			return;
		}

		if (config.getLoggerLevel() != null) {
			builder.logLevel(config.getLoggerLevel());
		}
		//設置connectTimeoutMillis和readTimeoutMillis的值,這裏的屬性值來自於.properties配置的
		if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
			builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout()));
		}

		if (config.getRetryer() != null) {
			Retryer retryer = getOrInstantiate(config.getRetryer());
			builder.retryer(retryer);
		}

		if (config.getErrorDecoder() != null) {
			ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
			builder.errorDecoder(errorDecoder);
		}

		if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
			// this will add request interceptor to builder, not replace existing
			for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
				RequestInterceptor interceptor = getOrInstantiate(bean);
				builder.requestInterceptor(interceptor);
			}
		}

		if (config.getDecode404() != null) {
			if (config.getDecode404()) {
				builder.decode404();
			}
		}

		if (Objects.nonNull(config.getEncoder())) {
			builder.encoder(getOrInstantiate(config.getEncoder()));
		}

		if (Objects.nonNull(config.getDecoder())) {
			builder.decoder(getOrInstantiate(config.getDecoder()));
		}

		if (Objects.nonNull(config.getContract())) {
			builder.contract(getOrInstantiate(config.getContract()));
		}
	}

把配置文件中的配置項初始化到FeignClientProperties中

  • 如果配置文件中沒有配置,則將FeignClientsConfiguration中的bean作爲默認值設置到builder.
  • 如果配置問你安中有配置,並且用默認加載順序時,首先加載FeignClientsConfiguration中的bean,然後加載在註解中配置configuration, 最後加載配置文件中的。
  • 如果不是默認加載順序,則首先加載註解中配置configuration,然後在從配置文件中配置,最後加載FeignClientsConfiguration中的配置。

如何生成代理對象…

org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {
		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?");
	}

org.springframework.cloud.openfeign.HystrixTargeter

public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
						Target.HardCodedTarget<T> target) {
		if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
			return feign.target(target);
		}
		feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
		SetterFactory setterFactory = getOptional(factory.getName(), context,
			SetterFactory.class);
		if (setterFactory != null) {
			builder.setterFactory(setterFactory);
		}
		Class<?> fallback = factory.getFallback();
		if (fallback != void.class) {
			return targetWithFallback(factory.getName(), context, target, builder, fallback);
		}
		Class<?> fallbackFactory = factory.getFallbackFactory();
		if (fallbackFactory != void.class) {
			return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
		}

		return feign.target(target);
	}

如果配置開啓容錯,則會採用默認
條件裝配

org.springframework.cloud.openfeign.FeignAutoConfiguration.HystrixFeignTargeterConfiguration

@Configuration
	@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
	protected static class HystrixFeignTargeterConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
			return new HystrixTargeter();
		}
	}

	@Configuration
	@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
	protected static class DefaultFeignTargeterConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public Targeter feignTargeter() {
			return new DefaultTargeter();
		}
	}

默認。。。。。

public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
						Target.HardCodedTarget<T> target) {
		return feign.target(target);
	}

Feign 如何。。。。。。使用

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

feign.ReflectiveFeign#newInstance

public <T> T newInstance(Target<T> target) {
// 爲每一個方法創建一個SynchronousMethodHandler對象,並放到map中
    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)) {
        // DefaultHandler
          DefaultMethodHandler handler = new DefaultMethodHandler(method);
          defaultMethodHandlers.add(handler);
          methodToHandler.put(method, handler);
        } else {
        // SynchronousXXX。。。
          methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
        }
      }
    }

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

總結:

  • 創建MethodHandler的映射。創建實現類SysnchronousMethodHandler
  • 通過InvacationHandlerFactory創建InvocationHandler
  • 綁定接口的default方法。通過DefaultMethodHandler綁定

feign.ReflectiveFeign.ParseHandlersByName#apply

  public Map<String, MethodHandler> apply(Target key) {
      List<MethodMetadata> metadata = this.contract.parseAndValidatateMetadata(key.type());
      Map<String, MethodHandler> result = new LinkedHashMap();

      MethodMetadata md;
      Object buildTemplate;
      for(Iterator var4 = metadata.iterator(); var4.hasNext(); result.put(md.configKey(), this.factory.create(key, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder))) {
        md = (MethodMetadata)var4.next();
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate = new ReflectiveFeign.BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new ReflectiveFeign.BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder);
        } else {
          buildTemplate = new ReflectiveFeign.BuildTemplateByResolvingArgs(md, this.queryMapEncoder);
        }
      }

      return result;
    }

feign.SynchronousMethodHandler.Factory#create

  public MethodHandler create(Target<?> target, MethodMetadata md, feign.RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) {
      return new SynchronousMethodHandler(target, this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, this.decode404, this.closeAfterDecode);
    }
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if (!"equals".equals(method.getName())) {
        if ("hashCode".equals(method.getName())) {
          return this.hashCode();
        } else {
          return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
        }
      } else {
        try {
          Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return this.equals(otherHandler);
        } catch (IllegalArgumentException var5) {
          return false;
        }
      }
    }
public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = this.buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();

    while(true) {
      try {
        return this.executeAndDecode(template);
      } catch (RetryableException var5) {
        retryer.continueOrPropagate(var5);
        if (this.logLevel != Level.NONE) {
          this.logger.logRetry(this.metadata.configKey(), this.logLevel);
        }
      }
    }
  }
Object executeAndDecode(RequestTemplate template) throws Throwable {
    Request request = this.targetRequest(template);
    if (this.logLevel != Level.NONE) {
      this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
    }

    long start = System.nanoTime();

    Response response;
    try {
      response = this.client.execute(request, this.options);
      response.toBuilder().request(request).build();
    } catch (IOException var15) {
      if (this.logLevel != Level.NONE) {
        this.logger.logIOException(this.metadata.configKey(), this.logLevel, var15, this.elapsedTime(start));
      }

      throw FeignException.errorExecuting(request, var15);
    }

    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
    boolean shouldClose = true;

    Object var10;
    try {
      if (this.logLevel != Level.NONE) {
        response = this.logger.logAndRebufferResponse(this.metadata.configKey(), this.logLevel, response, elapsedTime);
        response.toBuilder().request(request).build();
      }

      if (Response.class == this.metadata.returnType()) {
        Response var18;
        if (response.body() == null) {
          var18 = response;
          return var18;
        }

        if (response.body().length() != null && (long)response.body().length() <= 8192L) {
          byte[] bodyData = Util.toByteArray(response.body().asInputStream());
          Response var20 = response.toBuilder().body(bodyData).build();
          return var20;
        }

        shouldClose = false;
        var18 = response;
        return var18;
      }

      Object result;
      if (response.status() >= 200 && response.status() < 300) {
        if (Void.TYPE == this.metadata.returnType()) {
          result = null;
          return result;
        }

        result = this.decode(response);
        shouldClose = this.closeAfterDecode;
        var10 = result;
        return var10;
      }

      if (!this.decode404 || response.status() != 404 || Void.TYPE == this.metadata.returnType()) {
        throw this.errorDecoder.decode(this.metadata.configKey(), response);
      }

      result = this.decode(response);
      shouldClose = this.closeAfterDecode;
      var10 = result;
    } catch (IOException var16) {
      if (this.logLevel != Level.NONE) {
        this.logger.logIOException(this.metadata.configKey(), this.logLevel, var16, elapsedTime);
      }

      throw FeignException.errorReading(request, response, var16);
    } finally {
      if (shouldClose) {
        Util.ensureClosed(response.body());
      }

    }

    return var10;
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章