千家信息网

Feign中EnableFeignClients的作用是什么

发表于:2025-01-23 作者:千家信息网编辑
千家信息网最后更新 2025年01月23日,这期内容当中小编将会给大家带来有关Feign中EnableFeignClients的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。springcloud-
千家信息网最后更新 2025年01月23日Feign中EnableFeignClients的作用是什么

这期内容当中小编将会给大家带来有关Feign中EnableFeignClients的作用是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

springcloud-openfeign-core-2.1.1.release.

在springcloud中使用feign的时候,如下List-1,FeignClient的name是服务名称,会自动从Eureka中获取物理地址。

List-1

@FeignClient(name="UserProvider")public interface UserProvider {...}

Springcloud中这个是如何实现的?

这个要从@EnableFeignClients入手,如下List-2,Import注解引入了FeignClientsRegistrar--实现了ImportBeanDefinitionRegistrar接口,这样springboot会处理这个FeignClientsRegistrar。

List-2

@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 {};}

如下图1所示,FeignClientsRegistrar并没有复杂的继承关系,重点类看ImportBeanDefinitionRegistrar的registerBeanDefinitions实现。

图1

如下List-3,分俩个步骤,首先是registerDefaultConfiguration方法,将EnableFeignClients的defaultConfiguration注册到Spring容器中;之后是registerFeignClients方法将FeignClient注解的接口注册到Spring容器中。

List-3

@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,        BeanDefinitionRegistry registry) {    registerDefaultConfiguration(metadata, registry);    registerFeignClients(metadata, registry);}

来看registerDefaultConfiguration方法,如下List-4,

  1. 获取EnableFeignClients的所有属性,之后如果含有defaultConfiguration,则将defaultConfiguration注册到Spring容器中

  2. 方法registerClientConfiguration中,用Builder模式,构造FeignClientSpecification类型的BeanDefinition。FeignClientSpecification实现了NamedContextFactory.Specification接口,属性有个name和Class类型的configuration。

List-4

private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {    Map 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());}

registerFeignClients方法中,实现则较为复杂,如下List-5

List-5

public void registerFeignClients(AnnotationMetadata metadata,    BeanDefinitionRegistry registry) {    ClassPathScanningCandidateComponentProvider scanner = getScanner();//1    scanner.setResourceLoader(this.resourceLoader);    Set basePackages;    Map attrs = metadata            .getAnnotationAttributes(EnableFeignClients.class.getName());//2    AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(            FeignClient.class);//3    final Class[] clients = attrs == null ? null            : (Class[]) attrs.get("clients");    if (clients == null || clients.length == 0) {//4        scanner.addIncludeFilter(annotationTypeFilter);        basePackages = getBasePackages(metadata);    }    else {//5        final Set clientClasses = new HashSet<>();        basePackages = new HashSet<>();        for (Class clazz : clients) {            basePackages.add(ClassUtils.getPackageName(clazz));            clientClasses.add(clazz.getCanonicalName());        }        AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {            @Override            protected boolean match(ClassMetadata metadata) {                String cleaned = metadata.getClassName().replaceAll("\\$", ".");                return clientClasses.contains(cleaned);            }        };        scanner.addIncludeFilter(                new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));    }    for (String basePackage : basePackages) {        Set candidateComponents = scanner                .findCandidateComponents(basePackage);        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);                registerClientConfiguration(registry, name,                        attributes.get("configuration"));//6                registerFeignClient(registry, annotationMetadata, attributes);//7            }        }    }}
  1. 1处获取ClassPathScanner,用于扫描类路径

  2. 2处获取EnableFeignClients的所有属性

  3. 3处构造一个AnnotationTypeFilter,构造方法参数是FeignClient,这个用于过滤出只含有FeignClient的类

  4. 获得EnableFeignClients的clients属性值,4处如果是空,则获得EnableFeignClients所在的package路径(如果没有设置basePackageClasses)

  5. 5处,即EnableFeignClients的clients属性不是空,则遍历,放入集合中,同时获取client所在的package路面,加入到basePacakges中;构造AbstractClassTestingTypeFilter,这是增加一个过滤条件,即标FeignClient注解的接口,必须在EnableFeignClients的clients中

  6. 遍历basePackages,获取每个package下的符合条件的类,得到对应的beanDefinition,6处得到FeignClient的configuration值,通过FeignClientSpecification其注册到spring容器中,有意思的是这里检查了FeignClient注解的类须是接口,不然会报错。

  7. 7处将FeignClient注解的接口封装到FeignClientFactoryBean中,FactoryBean大家懂的,Spring中接口都封装到这个里面。

上述就是小编为大家分享的Feign中EnableFeignClients的作用是什么了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

0