栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Cloud Open Feign系列【13】@EnableFeignClients注解源码分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Cloud Open Feign系列【13】@EnableFeignClients注解源码分析

文章目录
  • 前言
  • 作用
  • 配置属性
  • FeignClientsRegistrar
  • 总结

前言

在Cloud 中使用@EnableFeignClients启用Feign客户端,接下来分析下这个注解的基本原理。

作用

@EnableFeignClients注解会扫描包路径下的@FeignClient注解定义的接口,并注册到IOC容器中。

配置属性

配置属性主要是配置扫描路径:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@documented
@import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
	// 配置扫描@FeignClient的路径,默认是当前标识了该注解类下的路径,和@ComponentScans一样
    String[] value() default {};
	// 扫描路径,多个
    String[] basePackages() default {};
	// 指定扫描某个类所在包下的所有类
    Class[] basePackageClasses() default {};
	// 指定客户端配置配
    Class[] defaultConfiguration() default {};
	// 直接指定扫描客户端的类,配置了就不会扫描
    Class[] clients() default {};
}
FeignClientsRegistrar

在@EnableFeignClients注解中,使用了@import注解导入了FeignClientsRegistrar,学过Spring 的应该知道@Configuration类上使用@import,可以注入Bean 对象。

FeignClientsRegistrar实现了以下几个接口:

  • importBeanDefinitionRegistrar:支持使用@import注解注册 BeanDefinition
  • ResourceLoaderAware:读取文件
  • EnvironmentAware:获取环境及配置属性

启动项目后,进行BeanDefinition注册时,就会进入到registerBeanDefinitions方法,该方法会注册默认配置和注册Feign 客户端。

    public void registerBeanDefinitions(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
        this.registerDefaultConfiguration(metadata, registry);
        this.registerFeignClients(metadata, registry);
    }

registerDefaultConfiguration会进行注册客户端配置类的BeanDefinition:

	// 参数为EnableFeignClients注解配置的类的信息、BeanDefinition注册器
    private void registerDefaultConfiguration(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
    	// 1. 获取EnableFeignClients注解上的配置属性
        Map defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
        	// 取个名字=》default.account.AccountApp
            String name;
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            } else {
                name = "default." + metadata.getClassName();
            }
			// 2. 注册客户端配置
            this.registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
        }

    }

接着进入到registerClientConfiguration,注册客户端配置,会在注册器中添加一个名字为default.account.AccountApp.FeignClientSpecification,类型为FeignClientSpecification的BeanDefinition。

    private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
    	// 创建一个 BeanDefinition构建者,使用FeignClientSpecification(这里存放了客户端配置名称和配置类)
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
        // 添加FeignClientSpecification 构造函数的参数
        // name => default.account.AccountApp
        // configuration=> 注解上的配置类,这里没有
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        // 注册,名称为=》default.account.AccountApp.FeignClientSpecification
        // 类型为=》FeignClientSpecification
        registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition());
    }

接着注册Feign 客户端,会调用扫描器,扫描类路径下的@FeignClient标记的类,然后读取配置信息,将该类的配置项、类信息都注册到注册器中。

    public void registerFeignClients(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
    	// BeanDefinition 不重复集合
        linkedHashSet candidateComponents = new linkedHashSet();
        // EnableFeignClients 注解配置信息
        Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        new AnnotationTypeFilter(FeignClient.class);
        Class[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
        // 查看是否配置了clients 属性,看来这个属性配置了以后,就不会进行包扫描
        if (clients != null && clients.length != 0) {
            Class[] var13 = clients;
            int var15 = clients.length;

            for(int var17 = 0; var17 < var15; ++var17) {
                Class clazz = var13[var17];
                candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
            }
        } else {
        	// 创建一个ClassPath扫描器
            ClassPathScanningCandidateComponentProvider scanner = this.getScanner();
            // 设置ResourceLoader
            scanner.setResourceLoader(this.resourceLoader);
            // 设置需要扫描的类,也就是配置了FeignClient 注解的类
            scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
            // 获取配置上的 这里没配置,就是扫描当前项目包下面的了
            Set basePackages = this.getbasePackages(metadata);
            Iterator var9 = basePackages.iterator();
			// 扫描到Feign 接口类,放入集合中
            while(var9.hasNext()) {
                String basePackage = (String)var9.next();
                candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
            }
        }

        Iterator var14 = candidateComponents.iterator();

        while(var14.hasNext()) {
        	// 循环扫描到的BeanDefinition  
            BeanDefinition candidateComponent = (BeanDefinition)var14.next();
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;
                // FeignClient注解上的元信息
                Annotationmetadata annotationmetadata = beanDefinition.getmetadata();
                Assert.isTrue(annotationmetadata.isInterface(), "@FeignClient can only be specified on an interface");
                // FeignClient注解配置的属性
                Map attributes = annotationmetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
                // BeanDefinition 名称=》order-service
                String name = this.getClientName(attributes);
                // 注册客户端配置=》configuration属性
                this.registerClientConfiguration(registry, name, attributes.get("configuration"));
                // 注册客户端
                this.registerFeignClient(registry, annotationmetadata, attributes);
            }
        }

    }

注册了客户端的配置后,就会注册客户端,调用的是registerFeignClient方法:

    private void registerFeignClient(BeanDefinitionRegistry registry, Annotationmetadata annotationmetadata, Map attributes) {
    	// account.OrderFeign
        String className = annotationmetadata.getClassName();
        // 创建FeignClientFactoryBean 类型的BeanDefinition 
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
        this.validate(attributes);
        // 将这些FeignClient 注解上的配置信息都添加到definition中,
        definition.addPropertyValue("url", this.getUrl(attributes));
       // 省略....
        beanDefinition.setPrimary(primary);
        String qualifier = this.getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }
		// 添加到注册器中
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

可以看到,最终每个Feign 接口注册到了 注册器中,名称为包名+接口名,Feign 接口实际生成的Bean对象为FeignClientFactoryBean,这是一个FactoryBean,由它最终生成的代理对象后续会讲解)。

总结

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/692893.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号