springboot2.x基礎教程:自動裝配原理與條件註解

程式設計師眾推發表於2020-09-10

spring Boot採用約定優於配置的方式,大量的減少了配置檔案的使用。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。
當springboot啟動的時候,預設在容器中注入許多AutoCongfigution類。在我們加入spring-boot-stareter-xx時,XXXAutoConfiguration類根據對應的條件,自動選擇裝配對應的Bean例項注入IOC容器中。

先說結論

  1. SpringBoot啟動的時候載入主配置類,開啟了自動配置功能@EnableAutoConfiguration
  2. @EnableAutoConfiguration Import的AutoConfigurationImportSelector中程式碼最終呼叫SpringFactoriesLoader.loadSpringFactories掃描了Jar包的META-INF/spring.factories檔案載入了大量的XXAutoConfiguration類
  3. AutoConfiguration類配合Conditonal註解與ConfigurationProperties配置類在特定條件下自動裝配我們需要的Bean到IOC容器中。

注入AutoConfiguration類核心原始碼分析

SpringBoot的主啟動類上需要加入@SpringBootApplication註解,我們看看該註解的原始碼。

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	//...省略
}

實際是@EnableAutoConfigurtaion註解起作用。

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
	Class<?>[] exclude() default {};
	String[] excludeName() default {};

}

看到@Import註解是不是很熟悉,該註解作用見教程@Enable原理,主要能夠匯入下面3種情況中的Bean。

  1. 允許注入使用@Configuration註解的類
  2. 允許使用實現ImportSelectorj介面的類,注入selectImports返回的類
  3. 允許是實現了ImportBeanDefinitionRegistrar介面的類

AutoConfigurationImportSelector中selectImports原始碼

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

其中AutoConfigurationImportSelector.getAutoConfigurationEntry呼叫AutoConfigurationImportSelector.getCandidateConfigurations呼叫SpringFactoriesLoader.loadFactoryNames呼叫SpringFactoriesLoader.loadSpringFactories

其中SpringFactoriesLoader.loadSpringFactories從指定的配置檔案META-INF/spring.factories載入配置。

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            try {
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryImplementationName = var9[var11];
                            result.add(factoryTypeName, factoryImplementationName.trim());
                        }
                    }
                }

                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

總結:

  1. @SpringBootApplication中@EnableAutoConfiguration註解最終呼叫SpringFactoriesLoader.loadSpringFactories,從classpath中搜尋所有的META-INF/spring.factories配置檔案
  2. 並將其中org.springframework.boot.autoconfigure.EnableutoConfiguration對應的配置項通過反射例項化為對應的標註了@Configuration的JavaConfig形式的IoC容器配置類,然後彙總為一個並載入到IoC容器。

條件註解

在spring-boot-autoconfigure包在META-INF/spring.factories檔案中autoconfiguration配置項一覽。

image-20200909232847766

我們拿DataSourceAutoConfiguration原始碼分析,看看autoconfiguration類生效的條件。

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

	@Configuration(proxyBeanMethods = false)
	@Conditional(EmbeddedDatabaseCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import(EmbeddedDataSourceConfiguration.class)
	protected static class EmbeddedDatabaseConfiguration {

	}

	@Configuration(proxyBeanMethods = false)
	@Conditional(PooledDataSourceCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
			DataSourceJmxConfiguration.class })
	protected static class PooledDataSourceConfiguration {

	}
	//省略....

}

在DataSourceAutoConfiguration類中,存在大量的@ConditionalXX條件註解,常見條件註解作用:

  1. @ConditionalOnBean:當SpringIoc容器記憶體在指定Bean的條件
  2. @ConditionalOnClass:當SpringIoc容器記憶體在指定Class的條件
  3. @ConditionalOnExpression:基於SpEL表示式作為判斷條件
  4. @ConditionalOnJava:基於JVM版本作為判斷條件
  5. @ConditionalOnJndi:在JNDI存在時查詢指定的位置
  6. @ConditionalOnMissingBean:當SpringIoc容器內不存在指定Bean的條件
  7. @ConditionalOnMissingClass:當SpringIoc容器內不存在指定Class的條件
  8. @ConditionalOnNotWebApplication:當前專案不是Web專案的條件
  9. @ConditionalOnProperty:指定的屬性是否有指定的值
  10. @ConditionalOnResource:類路徑是否有指定的值
  11. @ConditionalOnSingleCandidate:當指定Bean在SpringIoc容器內只有一個,或者雖然有多個但是指定首選的Bean
  12. @ConditionalOnWebApplication:當前專案是Web專案的條件
  13. @EnableConfigurationProperties註解:使使用 @ConfigurationProperties 註解的類生效。
    我們可以看在DataSourceAutoConfiguration啟用了DataSourceProperties配置,並且根據條件註解在不同情況下載入不同的資料來源到IOC容器中。

千里之行,始於足下。這裡是SpringBoot教程系列第十七篇,所有專案原始碼均可以在我的GitHub上面下載原始碼。覺得不錯可以評論、點贊、轉發3連。

相關文章