SpringCloud OpenFeign核心加载源码解析

目录

一、OpenFeign简介

  • Feign是一个声明式的web服务客户端,让编写web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可。
  • Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。
  • Feign可以与Nacos和Ribbon组合使用以支持负载均衡。

二、 源码加载流程图

版本说明:
Spring Cloud Version:Hoxton.SR12
Spring Boot Version:2.3.12.RELEASE
不同版本源码可能会有差异

在这里插入图片描述

三、OpenFeign中源码加载流程

我们一般采用@EnableFeignClients方式加载FeignClient,这里涉及到Spring生命周期加载问题不过多说明,有兴趣可以自行查阅。

1、@EnableFeignClients

OpenFeign使用@EnableFeignClients 才会开启OpenFeign,我们从下面这个注解进行切入,这个注解开启了FeignClient的解析过程

# value 需要扫描的路径,FeignClient存储的路径,不填默认扫描当前路径下所有文件夹中的FeignClient
@EnableFeignClients(value = "com.kerwin.client")

这个注解的声明如下,它用到了一个@Import注解,我们知道Import是用来导入一个配置类的,接下来去看一下FeignClientsRegistrar的定义。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
    
    
	// 省略属性
}

FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar,它是一个动态注入bean的接口,Spring Boot启动的时候,会去调用这个类中的registerBeanDefinitions来实现动态Bean的装载。它的作用类似于ImportSelector。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    
    
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
	BeanDefinitionRegistry registry) {
    
    
		//注册@EnableFeignClients中定义defaultConfiguration属性下的类,包装成FeignClientSpecification,注册到Spring的BeanDefinitionMap中。
		//在@FeignClient中有一个属性:configuration,这个属性是表示各个FeignClient自定义的配置类,后面也会通过调用registerClientConfiguration方法来注册成FeignClientSpecification到容器。
		//所以,这里可以完全理解在@EnableFeignClients中配置的是做为兜底的配置,在各个@FeignClient配置的就是自定义的情况。
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}
}

registerDefaultConfiguration 方法内部从 SpringBoot 启动类上检查是否有@EnableFeignClients, 有该注解的话, 则完成 Feign 框架相关的一些配置内容注册。

registerFeignClients 方法内部从 classpath 中, 扫描获得 @FeignClient 修饰的类, 将类的内容解析为 BeanDefinition , 最终通过调用 Spring 框架中的BeanDefinitionReaderUtils.resgisterBeanDefinition 将解析处理过的 FeignClientBeanDeifinition 添加到 spring 容器中。

2、registerFeignClients

registerFeignClients 方法,这个方法主要是扫描类路径下所有的@FeignClient注解,然后进行动态Bean的注入。它最终会调用 registerFeignClient 方法。

	// org.springframework.cloud.openfeign.FeignClientsRegistrar#registerFeignClients
	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");
		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));
			}
		}

		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);
				// 注册@FeignClient中设置的configuration配置
				registerClientConfiguration(registry, name,
						attributes.get("configuration"));
				// 注册FeignClient到Spring中
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}

	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);
		// BeanDefinitionBuilder是用来构建一个BeanDefinition的,它是通过genericBeanDefinition 来构建的,并且传入一个类型还有Supplier函数对象到BeanDefinition的instanceSupplier中,在后续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));
					}
					// 获取动态代理客户端
					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");

		beanDefinition.setPrimary(primary);

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

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

3、instanceSupplier.get() 实例化Bean

这一步是Spring的机制,这里要拿出来说一下,我看的这个版本的代码没有通过FactoryBean机制做加载,而是通过instanceSupplier来做的,在依赖注入FeignClient时会调用到AbstractAutowireCapableBeanFactory.createBeanInstance(),这个方法中第一个逻辑就是获取instanceSupplier做的实例化,实例化时执行instanceSupplier.get()方法就是去调用注册BeanDefinition时传入的Supplier函数对象,最终还是调用到FeignClientFactoryBean.getObject()。

	// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance
	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    
    
		// Make sure bean class is actually resolved at this point.
		Class<?> beanClass = resolveBeanClass(mbd, beanName);

		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
    
    
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}
		// 获取BeanDefinition中的instanceSupplier函数对象
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
    
    
			// 在这个方法中可以看的instanceSupplier.get()调用
			return obtainFromSupplier(instanceSupplier, beanName);
		}
		// 后面忽略
	}
	protected BeanWrapper obtainFromSupplier(Supplier<?> instanceSupplier, String beanName) {
    
    
		Object instance;

		String outerBean = this.currentlyCreatedBean.get();
		this.currentlyCreatedBean.set(beanName);
		try {
    
    
			// 实例化Bean
			instance = instanceSupplier.get();
		}
		// 后面忽略
	}

4、FeignClientFactoryBean.getObject()

FeignClient标注的这个接口,会通过FeignClientFactoryBean.getObject()这个方法获得一个代理对象,getObject调用的是getTarget方法,它从applicationContext取出FeignContext,FeignContext继承了NamedContextFactory,它是用来来统一维护feign中各个feign客户端相互隔离的上下文。

// org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
	@Override
	public Object getObject() {
    
    
		return getTarget();
	}
	<T> T getTarget() {
    
    
		// FeignContext注册到容器是在FeignAutoConfiguration上完成的
		// 在初始化FeignContext时,会把configurations在容器中放入FeignContext中。configurations的来源就是在前面registerFeignClient
		FeignContext context = beanFactory != null
				? beanFactory.getBean(FeignContext.class)
				: applicationContext.getBean(FeignContext.class);
		Feign.Builder builder = feign(context);
		// 判断是否需要LoadBalance,如果需要,则通过LoadBalance的方法来设置。
		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();
			return (T) loadBalance(builder, context,
					new HardCodedTarget<>(type, name, url));
		}
		//如果指定了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 LoadBalancerFeignClient) {
    
    
				// not load balancing because we have a url,
				// but ribbon is on the classpath, so unwrap
				client = ((LoadBalancerFeignClient) client).getDelegate();
			}
			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);
		}
		//生成默认代理类
		Targeter targeter = get(context, Targeter.class);
		// 最终调用
		return (T) targeter.target(this, builder, context,
				new HardCodedTarget<>(type, name, url));
	}

5、loadBalance

生成具备负载均衡能力的feign客户端,为feign客户端构建起绑定负载均衡客户端。

	// org.springframework.cloud.openfeign.FeignClientFactoryBean#loadBalance
	protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {
    
    
		// 从IOC容器中获取一个Client,注意这里的Client是根据contextId进行隔离的,每个contextId获取的Client是不同的,如果没有设置contextId就会使用serviceId获取一个Client
		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 or spring-cloud-starter-loadbalancer?");
	}

Client默认是LoadBalancerFeignClient,是在FeignRibbonClientAutoConfiguration中自动注册的。

@Import({
    
     HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })

protected <T> T loadBalance(Builder builder, FeignContext context,
	HardCodedTarget<T> target) {
    
    
		Client client = (Client)this.getOptional(context, Client.class);
		if (client != null) {
    
    
			builder.client(client);
			Targeter targeter = (Targeter)this.get(context, Targeter.class);
			return targeter.target(this, builder, context, target);
		} else {
    
    
			throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
	}
}

6、HystrixTargeter.target

最后执行的target方法,调用HystrixTargeter的target方法:

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget<T> target) {
    
    
		if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
    
    
			// 如果没有开启Hystrix会直接调用这里
			// 在FeignClientsConfiguration配置类中的静态内部类HystrixFeignConfiguration可以看到加载
			return feign.target(target);
		}
		feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
		String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
				: factory.getContextId();
		SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
		if (setterFactory != null) {
    
    
			builder.setterFactory(setterFactory);
		}
		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 feign.target(target);
	}

feign.target方法会调用一个build方法会返回一个ReflectiveFeign,携带者非常多的参数以及配置信息,最终调用了ReflectiveFeign的newInstance方法。

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

    public Feign build() {
    
    
      Client client = Capability.enrich(this.client, capabilities);
      Retryer retryer = Capability.enrich(this.retryer, capabilities);
      List<RequestInterceptor> 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 synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
    }
  }

7、ReflectiveFeign.newInstance生成代理

这个方法是用来创建一个动态代理的方法,在生成动态代理之前,会根据Contract协议(协议解析规则,解析接口类的注解信息,解析成内部的MethodHandler的处理方式。

从实现的代码中可以看到熟悉的Proxy.newProxyInstance方法产生代理类。而这里需要对每个定义的接口方法进行特定的处理实现,所以这里会出现一个MethodHandler的概念,就是对应方法级别的InvocationHandler。

  @Override
  // feign.ReflectiveFeign#newInstance
  public <T> T newInstance(Target<T> target) {
    
    
  	//根据接口类和Contract协议解析方式,解析接口类上的方法和注解,转换成内部的MethodHandler处理方式
    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)));
      }
    }
    // 基于Proxy.newProxyInstance 为接口类创建动态实现,将所有的请求转换给InvocationHandler 处理。
    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;
  }

创建的InvocationHandler 默认是FeignInvocationHandler,也就是说,最终注入的FeignClient接口,实际上调用目标方法时会调用FeignInvocationHandler的invoke方法

三、OpenFeign调用源码加载流程

以下是一个FeignClient的接口实例,当调用getInfo方法时,就会执行FeignInvocationHandler的invoke方法

@FeignClient(value = "kerwin-user",contextId = "userInfoClient")
public interface UserInfoClient {
    
    
    /**
     * 获取用户信息
     */
    @GetMapping("/user-info/info/{id}")
    String getInfo(@PathVariable("id") Long id);
}

1、FeignInvocationHandler.invoke

在invoke方法中,会调用 this.dispatch.get(method)).invoke(args) 。this.dispatch.get(method) 会返回一个SynchronousMethodHandler,进行拦截处理。

	// feign.ReflectiveFeign.FeignInvocationHandler#invoke
    @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();
      }
	  // 最终调用的是SynchronousMethodHandler的invoke方法
      return dispatch.get(method).invoke(args);
    }

2、SynchronousMethodHandler.invoke

这个方法会根据参数生成完成的RequestTemplate对象,还会封装连接请求超时时间Options还有重试处理器Retryer

  // feign.SynchronousMethodHandler#invoke
  @Override
  public Object invoke(Object[] argv) throws Throwable {
    
    
  	// template包含url、method元数据等等所有数据
    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;
      }
    }
  }

3、executeAndDecode

经过上述的代码,我们已经将restTemplate拼装完成,上面的代码中有一个 executeAndDecode() 方法,该方法通过RequestTemplate生成Request请求对象,然后利用Http Client获取response,来获取响应信息。

  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    
    
    //转化为Http请求报文
    Request request = targetRequest(template);

    if (logLevel != Logger.Level.NONE) {
    
    
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    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) {
    
    
      if (logLevel != Logger.Level.NONE) {
    
    
        logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
      }
      throw errorExecuting(request, e);
    }
    long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);


    if (decoder != null)
      return decoder.decode(response, metadata.returnType());

    CompletableFuture<Object> resultFuture = new CompletableFuture<>();
    asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
        metadata.returnType(),
        elapsedTime);

    try {
    
    
      if (!resultFuture.isDone())
        throw new IllegalStateException("Response handling not done");

      return resultFuture.join();
    } catch (CompletionException e) {
    
    
      Throwable cause = e.getCause();
      if (cause != null)
        throw cause;
      throw e;
    }
  }

猜你喜欢

转载自blog.csdn.net/weixin_44606481/article/details/132477936