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客户端:

@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients

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

	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		//注册默认的feign客户端配置类,所有的feign客户端可以公用这些配置
		registerDefaultConfiguration(metadata, registry);
		//注册feign客户端
		registerFeignClients(metadata, registry);
	}


	public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

		LinkedHashSet candidateComponents = new LinkedHashSet<>();
		Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
		final Class[] clients = attrs == null ? null : (Class[]) attrs.get("clients");
		//注解上的clients属性如果为空,则需要根据扫描的包去扫描带有FeignClient注解的类
		if (clients == null || clients.length == 0) {
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
			scanner.setResourceLoader(this.resourceLoader);
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
			Set basePackages = getBasePackages(metadata);
			for (String basePackage : basePackages) {
				candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
			}
		}
		else {
			//如果clients属性有值,则直接把指定的clients加入候选者
			for (Class clazz : clients) {
				candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
			}
		}
		//将每个候选者注册为FeignClient
		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 attributes = annotationMetadata
						.getAnnotationAttributes(FeignClient.class.getCanonicalName());

				String name = getClientName(attributes);
				//注册该FeignClient的配置类,其实是注册一个FeignClientSpecification,FeignClient注解上的configuration属性
				//将会作为构造器参数传入。注意每个FeignClient都有自己的spring上下文。
				registerClientConfiguration(registry, name, attributes.get("configuration"));
				//注册FeignClient,其实就是注册一个FactoryBean
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}


	private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
			Map 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
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
		factoryBean.setBeanFactory(beanFactory);
		factoryBean.setName(name);
		factoryBean.setContextId(contextId);
		factoryBean.setType(clazz);
		factoryBean.setRefreshableClient(isClientRefreshEnabled());
		//设置bean的instanceSupplier,指定spring bean实例化策略为supplier方式
		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));
			}
			//直接使用了getObject方法拿到真正的bean
			return factoryBean.getObject();
		});
		//指定按类型注入
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		//指定懒加载
		definition.setLazyInit(true);
		validate(attributes);

		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
		beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
		beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);

		// has a default, won't be null
		boolean primary = (Boolean) attributes.get("primary");
		//指定为首选bean
		beanDefinition.setPrimary(primary);

		String[] qualifiers = getQualifiers(attributes);
		if (ObjectUtils.isEmpty(qualifiers)) {
			qualifiers = new String[] { contextId + "FeignClient" };
		}

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
		//注册bean定义
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
		//注册可选的Request.Options,可以动态刷新Request配置,貌似不太常用
		registerOptionsBeanDefinition(registry, contextId);
	}

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

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


	 T getTarget() {
		//获取FeignContext,这其实是一个feign的上下文工厂,继承了Spring的NamedContextFactory
		//可以为每个feignClient创建一个独立的Spring上下文,FeignContext在FeignAutoConfiguration中自动装配
		FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
				: applicationContext.getBean(FeignContext.class);
		//获取一个Feign.Builder
		Feign.Builder builder = feign(context);

		if (!StringUtils.hasText(url)) {

			if (LOG.isInfoEnabled()) {
				LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
			}
			if (!name.startsWith("http")) {
				url = "http://" + name;
			}
			else {
				url = name;
			}
			url += cleanPath();
			//创建feign客户端
			return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
		}
		if (StringUtils.hasText(url) && !url.startsWith("http")) {
			url = "http://" + url;
		}
		String url = this.url + cleanPath();
		Client client = getOptional(context, 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(context, builder);

		Targeter targeter = get(context, Targeter.class);
		return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
	}

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

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

		// @formatter:off
		//从feignClient上下文获取Feign.Builder,在FeignClientsConfiguration中自动装配
		//如果没有引入断路器或是其它扩展组件,默认就是Feign.Builder
		//如果引入了circuitbreaker,则为FeignCircuitBreaker.Builder
		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
		//根据父容器的客户端配置配置feignClient
		//feignClient的配置优先级,默认情况下是
		//FeignClientProperties>FeignClientSpecification>自定义配置类>FeignClientsConfiguration
		//当然也可以用feign.client.defaultToProperties设置
		configureFeign(context, builder);

		return builder;
	}

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

	protected  T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget target) {
		//获取feign客户端,会在Feign.Builder中创建,默认是Client.Default
		//可以替换为像ApacheHttpClient等。
		//当然这里一般是FeignBlockingLoadBalancerClient,因为引入了spring cloud负载均衡器
		//FeignBlockingLoadBalancerClient是对Client.Default的包装,使客户端具有了负载均衡的能力
		Client client = getOptional(context, Client.class);
		if (client != null) {
			builder.client(client);
			//用FeignBuilderCustomizer配置Feign.Builder,如果有的话
			applyBuildCustomizers(context, builder);
			//从容器中获取Targeter,默认是DefaultTargeter
			//如果引入了断路器,则为FeignCircuitBreakerTargeter,会加入降级的逻辑fallback
			Targeter targeter = get(context, Targeter.class);
			//创建feign客户端
			return targeter.target(this, builder, context, target);
		}

		throw new IllegalStateException(
				"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
	}


	public  T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
			Target.HardCodedTarget target) {
		if (!(feign instanceof FeignCircuitBreaker.Builder)) {
			return feign.target(target);
		}
		//因为引入了断路器,把Feign.Builder强转为FeignCircuitBreaker.Builder
		FeignCircuitBreaker.Builder builder = (FeignCircuitBreaker.Builder) feign;
		String name = !StringUtils.hasText(factory.getContextId()) ? factory.getName() : factory.getContextId();
		//获取降级类或降级工厂
		Class fallback = factory.getFallback();
		if (fallback != void.class) {
			//以fallback 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);
	}


	private  T targetWithFallback(String feignClientName, FeignContext context, Target.HardCodedTarget target,
			FeignCircuitBreaker.Builder builder, Class fallback) {
		//获取fallback对象
		T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
		//FeignCircuitBreaker.Builder#target
		return builder(feignClientName, builder).target(target, fallbackInstance);
	}

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

		public Feign build(final FallbackFactory nullableFallbackFactory) {
			//创建Feign对象(ReflectiveFeign)
			//设置InvocationHandler工厂为FeignCircuitBreakerInvocationHandler
			super.invocationHandlerFactory((target, dispatch) -> new FeignCircuitBreakerInvocationHandler(
					circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory,
					circuitBreakerGroupEnabled, circuitBreakerNameResolver));
			//调用父类创建ReflectiveFeign
			return super.build();
		}

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

    public Feign build() {
      //在FeignClientFactoryBean#configureFeign中会把容器中的Capability设置进来
      //Capability对象可以增强Feign的各个组件,一般就是对原有的组件的包装
      Client client = Capability.enrich(this.client, capabilities);
      Retryer retryer = Capability.enrich(this.retryer, capabilities);
      List requestInterceptors = this.requestInterceptors.stream()
          .map(ri -> Capability.enrich(ri, capabilities))
          .collect(Collectors.toList());
      Logger logger = Capability.enrich(this.logger, capabilities);
      Contract contract = Capability.enrich(this.contract, capabilities);
      Options options = Capability.enrich(this.options, capabilities);
      Encoder encoder = Capability.enrich(this.encoder, capabilities);
      Decoder decoder = Capability.enrich(this.decoder, capabilities);
      InvocationHandlerFactory invocationHandlerFactory =
          Capability.enrich(this.invocationHandlerFactory, capabilities);
      QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
      //SynchronousMethodHandler.Factory,用来创建MethodHandler的工厂
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
      //ParseHandlersByName,用来创建方法名与对应MethodHandler的映射关系
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
  }

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

  public  T newInstance(Target target) {
    //这里的target为HardCodedTarget,包含了该feign客户端的接口类型,name,url
    //用ParseHandlersByName创建方法名和MethodHandler的映射map
    Map nameToHandler = targetToHandlersByName.apply(target);
    //Method对象和MethodHandler的映射map
    Map methodToHandler = new LinkedHashMap();
    List defaultMethodHandlers = new LinkedList();

    //遍历接口的每个方法
    for (Method method : target.type().getMethods()) {
      if (method.getDeclaringClass() == Object.class) {
        continue;
      } else if (Util.isDefault(method)) {
        //如果是default方法,则用DefaultMethodHandler处理,这种方法会使用java7的MethodHandle调用
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
        //如果是自定义的方法,也就是我们自己写的方法则从nameToHandler里面获取对应的SynchronousMethodHandler
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    //jdk动态代理的InvocationHandler,这里为FeignCircuitBreakerInvocationHandler
    InvocationHandler handler = factory.create(target, methodToHandler);
    //创建动态代理
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      //将MethodHandle绑定到代理对象
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }

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

未完待续。。。

你可能感兴趣的:(feign,spring,cloud,feign)