Spring-Cloud-OpenFeign源码解析-02-OpenFeign自动装配

@EnableFeignClients注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
    
	String[] value() default {};

	String[] basePackages() default {};

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

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

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

}

通过@Import注解导入FeignClientsRegistrar

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
}

可以看到该类实现了ImportBeanDefinitionRegistrar接口

ImportBeanDefinitionRegistrar 接口允许开发者在运行时动态地向 Spring 应用程序上下文中注册 Bean 定义,这个接口通常与 @Import 注解结合使用,当 Spring 容器扫描到带有 @Import 注解的类时,会调用实现了ImportBeanDefinitionRegistrar接口的类registerBeanDefinitions方法。

开发者可以编写自定义逻辑来创建BeanDefinition对象(这些对象描述了如何创建Bean实例),并使用BeanDefinitionRegistry将它们注册到Spring容器中

FeignClientsRegistrar#registerBeanDefinitions()
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 处理@EnableFeignClients注解上的属性配置,将配置注册到容器中
		registerDefaultConfiguration(metadata, registry);
        // 注册@FeignClient对应的接口,加入到容器中
		registerFeignClients(metadata, registry);
	}

FeignClientsRegistrar#registerFeignClients()

	public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
        //获取@EnableFeignClients的属性集合
		Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        //获取clients属性
		final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
		if (clients == null || clients.length == 0) {
            //拿到spring内置的扫描器
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
			scanner.setResourceLoader(this.resourceLoader);
            //添加过滤器:过滤器所有被 @FeignClient 标记的接口
			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));
			}
		}
		//循环处理@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");
                //获取@FeignClient注解的属性集合
				Map<String, Object> attributes = annotationMetadata
						.getAnnotationAttributes(FeignClient.class.getCanonicalName());
                //获取client的名称
				String name = getClientName(attributes);
				registerClientConfiguration(registry, name, attributes.get("configuration"));
                //核心方法,注入FeignClient对象
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}

第一部分是扫描@Feignclient注解的接口类,第二部分就是对扫描出的接口类进行处理,主要关注registerClientConfigurationregisterFeignClient函数。

其中registerClientConfiguration是为了处理@FeignClient#configuration属性的,在这个函数会往Spring容器中添加#{serviceName}.FeignClientSpecification作为名字的FeignClientSpecification类对象

registerFeignClient函数则是将第一步扫描出来的接口注入到Spring容器中。

接下来我们看一下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;
		String contextId = getContextId(beanFactory, attributes);
		String name = getName(attributes);
        //创建FeignClientFactoryBean对象,下一篇会重点分析FeignClientFactoryBean
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
		factoryBean.setBeanFactory(beanFactory);
		factoryBean.setName(name);
		factoryBean.setContextId(contextId);
		factoryBean.setType(clazz);
		factoryBean.setRefreshableClient(isClientRefreshEnabled());
        //通过BeanDefinitionBuilder创建对象
		BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
            //设置feign接口参数,包括访问路径、fallback等
			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));
			}
			//最终调用的是factoryBean.getObject()方法
			return factoryBean.getObject();
		});
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		definition.setLazyInit(true);
		validate(attributes);
		//获取BeanDefinition对象
		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);
        //调用registerBeanDefinition方法注入Spring容器中
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

		registerOptionsBeanDefinition(registry, contextId);
	}

看一下registerBeanDefinition方法

	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
        //通过Spring提供的注册器将FeignClient接口包装成的BeanDefinition注入到容器中
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
				registry.registerAlias(beanName, alias);
			}
		}
	}

总结

通过@EnableFeignClients中的@ImportFeignClientsRegistrar配置,当Spring容器扫描到带有 @Import 注解的类时,会调用实现了ImportBeanDefinitionRegistrar接口的类registerBeanDefinitions方法。从而扫描配置包下面的所有@FeigntClient注解的接口,构建成一个 FeignClientFactoryBean对象,包装为BeanDefinition并注入到Spring容器中。

相关推荐

  1. Spring-Cloud-OpenFeign解析-02-OpenFeign自动装配

    2024-05-16 01:26:04       12 阅读
  2. Spring Cloud——OpenFeign

    2024-05-16 01:26:04       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-16 01:26:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-16 01:26:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-16 01:26:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-16 01:26:04       18 阅读

热门阅读

  1. 【鱼眼+普通相机】相机标定

    2024-05-16 01:26:04       9 阅读
  2. FastAdmin菜单规则树形结构分类显示

    2024-05-16 01:26:04       9 阅读
  3. 第十一周学习笔记DAY.1-MySQL

    2024-05-16 01:26:04       9 阅读
  4. mysql 索引失效的原因

    2024-05-16 01:26:04       11 阅读
  5. 设计模式:备忘录模式

    2024-05-16 01:26:04       11 阅读
  6. 数据特征降维 | 主成分分析(PCA)附Python代码

    2024-05-16 01:26:04       11 阅读