OpenFeign 工作原理源码记录

本文源码基于 spring-cloud-starter-openfeign-3.1.8

@EnableFeignClients

点开@EnableFeignClients,可以看到@EnableFeignClients导入了一个FeignClientsRegistrar类:@Import(FeignClientsRegistrar.class),这个类继承了ImportBeanDefinitionRegistrar接口。关于@Import的原理可以看这篇文章:
https://blog.csdn.net/qq_40926260/article/details/142646615?spm=1001.2014.3001.5502

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
   
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   
		// 注册 FeignClientSpecification.class
		registerDefaultConfiguration(metadata, registry);
		// 
		registerFeignClients(metadata, registry);
	}
}

registerDefaultConfiguration

registerDefaultConfiguration方法的作用在于向容器中注册FeignClientSpecification这个BeanDefinition

// metadata:@EnableFeignClients 加在哪里,metadata 就是哪个类的注解元数据
// registry: Bean 注册中心,即 Spring 容器
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry c) {
   
	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"));
	}
}

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

defaultConfiguration这个配置的作用在于FeignClientSpecification这个类有一个带参构造,会使用到这个参数
在这里插入图片描述

registerFeignClients

registerFeignClients方法意为注册所有的FeignClient

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) {
   
		// 扫描类路径下的 @FeignClient
		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 {
   
		// 禁用类路径下的 @FeignClient 扫描
		for (Class<?> clazz : clients) {
   
			candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
		}
	}

	// 得到所有的@FeignClient注解类型的Bean定义
	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);
			registerClientConfiguration(registry, name, attributes.get("configuration"));

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

类路径扫描

@EnableFeignClients注解的clients属性如果不为空,那么会进行类路径扫描,使用配置的FeignClient类型

类路径扫描用到了@EnableFeignClients的 3 个属性

  1. value:包名配置
  2. basePackages:包名配置
  3. basePackageClasses:使用指定Class的包名
    如果 @EnableFeignClients 上这3种方式都没配置,才使用@EnableFeignClients加在的那个类的包名作为包扫描路径
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
   
	Map<String, Object> attributes = importingClassMetadata
			.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());

	Set<String> basePackages = new HashSet<>();
	for (String pkg : (String[]) attributes.get("value")) {
   
		if (StringUtils.hasText(pkg)) {
   
			basePackages.add(pkg);
		}
	}
	for (String pkg : (String[]) attributes.get("basePackages")) {
   
		if (StringUtils.hasText(pkg)) {
   
			basePackages.add(pkg);
		}
	}
	for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
   
		basePackages.add(ClassUtils.getPackageName(clazz));
	}
	// 如果 @EnableFeignClients 上一个包名都没配置,才使用@EnableFeignClients加在的那个类的包名
	if (basePackages.isEmpty()) {
   
		basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
	}
	return basePackages;
}

注册 @FeignClient

客户端名称的优先级,contextId -> name -> serviceId,如果都没有则报错

private String getClientName(Map<String, Object> client) {
   
	if (client == null) {
   
		return null;
	}
	String value = (String) client.get("contextId");
	if (!StringUtils.hasText(value)) {
   
		value = (String) client.get("value");
	}
	if (!StringUtils.hasText(value)) {
   
		value = (String) client.get("name");
	}
	if (!StringUtils.hasText(value)) {
   
		value = (String) client.get("serviceId");
	}
	if (StringUtils.hasText(value)) {
   
		return value;
	}

	throw new IllegalStateException(
			"Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
}

这个客户端名称是FeignClientSpecification所需的另外一个参数
在这里插入图片描述
注册@EnableFeignClients@FeignClient都是注册的FeignClientSpecification这个类型,只是 Bean 的名称不同,形式都是:${name}.FeignClientSpecification

registerFeignClient
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;
	// 获取 contextId ,支持表达式计算
	String contextId = getContextId(beanFactory, attributes);
	// serviceId -> name -> value,支持表达式计算
	String name = getName(attributes);
	FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
	factoryBean.setBeanFactory(beanFactory);
	factoryBean.setName(name);
	factoryBean.setContextId(contextId);
	factoryBean.setType(clazz);
	factoryBean.setRefreshableClient(isClientRefreshEnabled());
	BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
   
		factoryBean.setUrl(getUrl(beanFactory, attributes));
		factoryBean.setPath(getPath(beanFactory, attributes));
		factoryBean
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值