【Feign】Feign源码分析(一): Feign配置类的BeanDifinition注册

106 阅读2分钟

一.源码入口

  1. 开启Feign客户端的核心注解为:@EnableFeignClients,表示该项目能够被声明为Feign的客户端

@Slf4j
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClientspublic class NnDataApplication {
    public static void main(String[] args) throws UnknownHostException {
        ConfigurableApplicationContext application = SpringApplication.run(NnDataApplication.class, args);
}

2.@EnableFeignClients中的核心类是  FeignClientsRegistrar ,可以看到核心方法  registerBeanDefinitions()

    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
         //2.1 注册Feign相关的配置
        registerDefaultConfiguration(metadata, registry);
         //2.2 注册FeignClient对象
        registerFeignClients(metadata, registry);
    }

2.1.将FeignClients的默认的配置属性封装成 FeignClientSpecification 的BeanDifinition对象

    private void registerDefaultConfiguration(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //解析EnableFeignClients注解中的defaultAttrs默认属性,封装成Map
        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();
            }
         //注册FeignClientSpecification 的 BeanDifinition对象
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }

2.2.解析指定路径(或默认路径下的)@FeignClient注解的属性和接口,将FeignClient对象封装成  FeignClientFactoryBean类的BeanDifinition对象,核心方法

    public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        //获取类路径扫描器,作用是:扫描指定路径或者默认路径下的被@FeignClient注解修饰的接口以及@FeignClient中的属性
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        //定义包扫描路径
        Set<String> basePackages;
     //从@EnableFeignClient注解上获取所有的属性,封装成Map
        Map<String, Object> attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        //注解过滤器,作用是,在扫描路径下 过滤出被@FeignClient修饰的接口
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
       
        //解析@EnableFeignClient中的clients属性(client属性主要是指定被@FeignClient修饰的类,指定该属性会覆盖包扫描路径下的被@FeignClient修饰的类)
        final Class<?>[] clients = attrs == null ? null
                : (Class<?>[]) attrs.get("clients");
        //指定了clients属性
        if (clients == null || clients.length == 0) {
            scanner.addIncludeFilter(annotationTypeFilter);
            //从Map中 ,获取包路径
            basePackages = getBasePackages(metadata);
        }
        //没有指定clients属性
        else {
            final Set<String> 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)));
        }
        //遍历处理好的包路径集合 basePackages
 
       for (String basePackage : basePackages) {
            Set<BeanDefinition> candidateComponents = scanner
                    .findCandidateComponents(basePackage);
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // verify annotated class is an interface 校验被@FeignClient修饰的类是否是接口
                    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());
                    //从属性中获取Client的名称
                    String name = getClientName(attributes);
                    //注册FeignClient中的配置信息,将配置信息注册为FeignClientSpecification 类型的BeanDifinition对象,参考2.1
                    registerClientConfiguration(registry, name,
                            attributes.get("configuration"));
                    //将  1.BeanDifinition注册器,2.包路径的元数据信息,3.@FeignClient中的属性的信息 通过registerFeignClient()方法注册为 FeignClientFactoryBean类的BeanDifinition对象
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }

2.2.1.注册FeignClientFactoryBean的BD对象

private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        //获取类名
        String className = annotationMetadata.getClassName();
        //构建 FeignClientFactoryBean对象,该对象实现了 Spring框架中的FactoryBean对象,在实例化时会调用getObject()方法将相关的对象注册到Spring容器中
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
        validate(attributes);
        //将@FeignClient上的属性封装到BD对象中
        definition.addPropertyValue("url", getUrl(attributes));
        definition.addPropertyValue("path", getPath(attributes));
        String name = getName(attributes);
        definition.addPropertyValue("name", name);
        String contextId = getContextId(attributes);
        definition.addPropertyValue("contextId", contextId);
        definition.addPropertyValue("type", className);
        definition.addPropertyValue("decode404", attributes.get("decode404"));
        definition.addPropertyValue("fallback", attributes.get("fallback"));
        definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

        boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
                                                                // null

        beanDefinition.setPrimary(primary);

        String qualifier = getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                new String[] { alias });
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }