前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Feign自动装配原理

Feign自动装配原理

作者头像
Java学习录
发布2019-11-12 14:06:28
8100
发布2019-11-12 14:06:28
举报
文章被收录于专栏:Java学习录Java学习录
spring.factories

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

代码语言:javascript
复制
@Autowired(required = false)    private List<FeignClientSpecification> 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方法吧

代码语言:javascript
复制
   public void registerBeanDefinitions(AnnotationMetadata metadata,            BeanDefinitionRegistry registry) {        registerDefaultConfiguration(metadata, registry);         registerFeignClients(metadata, registry);    }

这里分为了2步

注册缺省配置
代码语言:javascript
复制
private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {        // 获取注解@EnableFeignClients的注解属性             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();            }            // 各种信息准备就绪,现在执行注册            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客户端
代码语言:javascript
复制
public void registerFeignClients(AnnotationMetadata metadata,                                 BeanDefinitionRegistry registry) {    ClassPathScanningCandidateComponentProvider scanner = getScanner();    scanner.setResourceLoader(this.resourceLoader);
    Set<String> basePackages;
    Map<String, Object> 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<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)));    }

    for (String basePackage : basePackages) {        Set<BeanDefinition> 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<String, Object> 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客户端注册
代码语言:javascript
复制
private void registerFeignClient(BeanDefinitionRegistry registry,                                 AnnotationMetadata annotationMetadata, Map<String, Object> 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的核心应该也在这个类里面了

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java学习录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • spring.factories
  • @EnableFeignClients注解解析
    • 注册缺省配置
      • 注册各个Feign客户端
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档