Feign源码解析——初始化流程

Feign源码解析——初始化流程

首先我们在使用Feign的时候会加上两个注解,第一个是@EnableFeignClients开启Feign的功能,第二个是在修饰被调用方的接口加上这个注解@FeignClient而生成代理类。那么我们就根据这两个注解为入口进行解析。

@EnableFeignClients注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
        …………
}

@Import注解就是之前xml配置中的import标签,可以用于依赖第三方包中bean的配置和加载

进入@EnableFeignClients注解我们可以发现里面加载了FeignClientsRegistrar类,进去FeignClientsRegistrar类,我们看到他实现了ImportBeanDefinitionRegistrar这个类,ImportBeanDefinitionRegistrar接口是Spring的一个扩展接口,通过registerBeanDefinitions方法向容器中注入Bean。

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
        ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
    ……………………………省略…………………………………
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        registerDefaultConfiguration(metadata, registry);
        registerFeignClients(metadata, registry);
    }
    ………………………………省略……………………………………
}

所以我们可以理解这一步就是将程序中带有@FeignClient注解的接口注册到Spring容器中。接下来我们深入了解他是如何将什么信息注册进去了

其中registerDefaultConfiguration方法注册了@EnableFeignClients注解中defaultConfiguration属性,重点我们看registerFeignClients方法。


public void registerFeignClients(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);

        Set basePackages;
        //取出EnableFeignClients注解中的信息
        Map attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        //设置要扫描的注解是@FeignClient
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
        //查看@EnableFeignClients中设置的clients参数
        final Class[] clients = attrs == null ? null
                : (Class[]) attrs.get("clients");
        //如果没有设置clients参数,那么设置要扫描的包是@EnableFeignClients中设置的value值
        if (clients == null || clients.length == 0) {
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = getBasePackages(metadata);
        }
        else {
            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) {
            //扫描设置的包中的@FeignClient注解
            Set candidateComponents = scanner
                    .findCandidateComponents(basePackage);
            for (BeanDefinition candidateComponent : candidateComponents) {
                if (candidateComponent instanceof AnnotatedBeanDefinition) {
                    // 验证@ FeignClient注解的类是一个接口
                    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                    Assert.isTrue(annotationMetadata.isInterface(),
                            "@FeignClient can only be specified on an interface");
                    //获得@ FeignClient所设置的属性
                    Map attributes = annotationMetadata
                            .getAnnotationAttributes(
                                    FeignClient.class.getCanonicalName());
                    //获得@FeignClient中设置的value值
                    String name = getClientName(attributes);
                    //将@FeignClient注解中Configuration属性注册进容器中,其中名字是name的值
                    registerClientConfiguration(registry, name,
                            attributes.get("configuration"));
                    //下面有讲解
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }

registerFeignClient方法讲解,其实这一块就是将@FeignClient注解中所有的value值取出并且放入容器中。在这里我们需要注意此时注册是FeignClientFactoryBean类型,之前注册的都是FeignClientSpecification类型的

private void registerFeignClient(BeanDefinitionRegistry registry,
            AnnotationMetadata annotationMetadata, Map attributes) {
        String className = annotationMetadata.getClassName();
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientFactoryBean.class);
        validate(attributes);
        definition.addPropertyValue("url", getUrl(attributes));
        definition.addPropertyValue("path", getPath(attributes));
        String name = getName(attributes);
        definition.addPropertyValue("name", name);
        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 = name + "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);
    }

到这Feign的初始化流程已经说完了。大概总结一下Feign的初始化流程

  1. 读取@EnableFeignClients注解中basePackage值下的所有带有@FeignClient的接口
  2. 读取接口上面的 @FeignClient 注解参数
  3. 如果此接口上有Configuration参数,那么先进行注册此参数,注意此参数注册在Spring容器中是以FeignClientSpecification类型注册的
  4. 注册完Configuration参数以后,然后将其余的信息注册到容器中,注意这时是以FeignClientFactoryBean类型注册的,另外此时的Configuration参数并没有传过来。

参考文章

  • https://xli1224.github.io/2017/09/14/feign-anaylsis/
  • https://blog.csdn.net/forezp/article/details/73480304
  • https://www.jianshu.com/p/f72d89265166

你可能感兴趣的:(Feign源码解析——初始化流程)