Spring Boot自动装配原理(2)— Spring Cloud Alibaba微服务原理与实战笔记

Spring Boot自动装配原理(2)— Spring Cloud Alibaba微服务原理与实战笔记

上一节说到@Import注解导入的是一个AutoConfigurationImportSelector类,那么它一定会实现配置类的导入,那么这一节我们具体分析。

阅读时间预计:15min 😃

AutoConfigurationImportSelector

AutoConfigurationImportSelector实现了ImportSelector,而ImportSelector接口里面的selectImports方法返回一个String[]数组,这个数组可以指定需要装配到IoC容器的类,当在@Import中导入一个ImportSelector的实现类后,会把该实现类返回的Class名称都装在到IoC容器中。

public interface ImportSelector {
    String[] selectImports(AnnotationMetadata var1);

    @Nullable
    default Predicate<String> getExclusionFilter() {
        return null;
    }
}

和@Configuration不同的是,ImportSelector可以实现批量装配,还可以通过逻辑处理来实现Bean的选择性装配,也就是时候可以根据上下文来决定哪些类能够被IoC容器初始化。

自动装配实现源码详解

首先,通过之前的章节可以猜想到,自动装配的核心是扫描约定目录下的文件进行解析,解析完成之后把得到的Configuration配置类通过ImportSelector进行导入,从而完成Bean的自动装配过程。

首先我们定位到AutoConfigurationImportSelector的selectImports方法,它是ImportSelector接口的实现,这个方法主要有两个功能:

  • AutoConfigurationMetadataLoader.loadMetadata从META-INF/spring-autoconfigure-metadata.properties中加载自动装配的条件元数据,简单来说就是只有满足条件的Bean才能进行装配。

    org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration=
    org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
    org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,reactor.core.publisher.Flux,org.springframework.data.cassandra.core.ReactiveCassandraTemplate
    org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository
    org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration.ConditionalOnWebApplication=SERVLETa
    //省略部分
    

    它是Bean装配前的条件配置,减少配置类的加载数量,加速启动时间。

  • 收集所有符合条件的配置类autoConfigurationEntry.getConfigurations(),完成自动装配。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

我们重点分析一下配置类的收集方法getAutoConfigurationEntry,根据之前的猜想,这个方法实现的也应该是扫描指定路径下的文件解析得到需要装配的配置类,而这里用到了SpringFactoriesLoader。我们先分析这个收集方法:

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.filter(configurations, autoConfigurationMetadata);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

它主要做以下几件事:

  • getAttributes获得@EnableAutoConfiguration注解中的属性exclude、excludeName等。
  • getCandidateConfigurations(获取候选配置)获得所有自动装配的配置类,后面重点分析。
  • removeDuplicates去除重复的配置项。
  • getExclusions根据@EnableAutoConfiguration注解中配置的exclude等属性,把不需要自动装配的配置类移除。
  • fireAutoConfigurationImportEvents广播事件。
  • 最后经过多层判断和过滤之后的配置类集合。

总的来说,它先获得所有的配置类,然后进行去重、exclude排除等操作,得到最终需要实现自动装配的配置类。这里需要重点关注的是getCandidateConfigurations,它是获得配置类最核心的方法:

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
        Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

它用到了SpringFactoriesLoader,这是Spring内部提供的一种约定俗成的加载方式,类似于Java中的SPI。

简单来说:它会扫描classpath下的META-INF/spring.factories文件,里面的数据以key-value的方式存储,而SpringFactoriesLoader.loadFactoryNames会根据key得到value。因此,在这个场景中key就是EnableAutoConfiguration,value就是多个配置类,也就是getCandidateConfigurations返回的值。

# org.springframework.boot.autoconfigure 目录下的 META-INF/spring.factories
//省略部分
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
//省略部分

总结

至此,自动装配的原理基本就分析完成了,简单来总结一下核心过程:

  • 通过@Import({AutoConfigurationImportSelector.class})实现配置类的导入,但这不是Spring传统意义上的单个配置类装配。
  • AutoConfigurationImportSelector实现了ImportSelector接口,重写了方法selectImports,它用于实现选择性批量配置类的装配。
  • 通过Spring提供的SpringFactoriesLoader机制,扫描classpath路径下的META-INF/spring.factories,读取需要实现自动装配的配置类。
  • 通过条件筛选的方式,把不符合条件的配置类移除,最终完成自动装配。

下次我们手写一个自己的Starter~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值