精选文章 Feign自动装配原理

Feign自动装配原理

作者:javaxuexilu 时间: 2019-11-04 08:08:00
javaxuexilu 2019-11-04 08:08:00
spring.factories

按照以往的惯例,在研究源码的时候,我们先看一下spring.factories文件下自动装配的类FeignAutoConfiguration,其中比较重要的东西有这么几个

@Autowired(required = false)	
    private List configurations = new ArrayList<>();	

	

	
    @Bean	
    public FeignContext feignContext() {	
        FeignContext context = new FeignContext();	
        context.setConfigurations(this.configurations);	
        return context;	
    }	

	
    @Configuration	
    @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")	
    protected static class HystrixFeignTargeterConfiguration {	
        @Bean	
        @ConditionalOnMissingBean	
        public Targeter feignTargeter() {	
            return new HystrixTargeter();	
        }	
    }	

	
    @Configuration	
    @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")	
    protected static class DefaultFeignTargeterConfiguration {	
        @Bean	
        @ConditionalOnMissingBean	
        public Targeter feignTargeter() {	
            return new DefaultTargeter();	
        }	
    }
  1. 属性configurations代表的是各个Feign客户端的配置类,这个稍后会再次提到

  2. FeignContext这个bean看名字就知道,Feign的上下文环境,包含了所有feign客户端的配置

  3. 接下来是两个Targeter是看当前是否存在hystrix环境,接下来也会提到

  4. 除此之外这个类还包含了HttpClient相关的配置就不展开了


@EnableFeignClients注解解析

查看完自动装配的类,接着看@EnableFeignClients注解

进入这个注解发现,它引入了配置类 FeignClientsRegistrar,由于这个类实现了ImportBeanDefinitionRegistrar接口,所以按照我们以往的经历直接看一下registerBeanDefinitions方法吧

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

这里分为了2步

注册缺省配置
private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {	
        // 获取注解@EnableFeignClients的注解属性     	
        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());	
    }

可以看到这里就是处理注册@EnableFeignClients上defaultConfiguration属性所指定的客户端的缺省配置,注意这里配置都是注册为了FeignClientSpecification类型的bean,这个类型的bean也是本文刚开始提到的被Feign上下文持有的各个Feign客户端持有的

注册各个Feign客户端
public void registerFeignClients(AnnotationMetadata metadata,	
                                 BeanDefinitionRegistry registry) {	
    ClassPathScanningCandidateComponentProvider scanner = getScanner();	
    scanner.setResourceLoader(this.resourceLoader);	

	
    Set basePackages;	

	
    Map attrs = metadata	
        .getAnnotationAttributes(EnableFeignClients.class.getName());	
    AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(	
        FeignClient.class);	
    final Class[] clients = attrs == null ? null	
        : (Class[]) attrs.get("clients");	
    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) {	
        Set candidateComponents = scanner	
            .findCandidateComponents(basePackage);	
        for (BeanDefinition candidateComponent : candidateComponents) {	
            if (candidateComponent instanceof AnnotatedBeanDefinition) {	
                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"));	
                 registerFeignClient(registry, annotationMetadata, attributes);	
            }	
        }	
    }

这里一共分为3个步骤:

  1. 使用ClassPathScanningCandidateComponentProvider扫描所有的标注了@FeignClient注解的接口

  2. 将注解上包含的属性作为bean注册,这些属性也就是每个Feign客户端端的配置

  3. @Feign客户端注册

private void registerFeignClient(BeanDefinitionRegistry registry,	
                                 AnnotationMetadata annotationMetadata, Map attributes) {	
    // 1.获取标注@Feign注解的接口名称	
    String className = annotationMetadata.getClassName();	

	
    // 2.使用BeanDefinitionBuilder构造bean:FeignClientFactoryBean	
    BeanDefinitionBuilder definition = BeanDefinitionBuilder	
        .genericBeanDefinition(FeignClientFactoryBean.class);	
    validate(attributes);	

	
    // 3.添加FeignClientFactoryBean的各个属性	
    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);	

	
    // 4.设置别名	
    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;	
    }	

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

上方这个方法通过了5步把各个FeignClient都注册成了bean:FeignClientFactoryBean,相信看过之前文章的同学都知道FactoryBean系列的bean是干什么的了。而Feign整合Ribbon和Hystrix的核心应该也在这个类里面了

往期好文

勿删,copyright占位
分享文章到微博
分享文章到朋友圈

上一篇:LeetCode-Python-1248. 统计「优美子数组」(数组 + 数学)

下一篇:数学“水课”产生的历史根源

CSDN

CSDN

中国开发者社区CSDN (Chinese Software Developer Network) 创立于1999年,致力为中国开发者提供知识传播、在线学习、职业发展等全生命周期服务。