SpringBoot作為當前Java開發的熱門框架,有絞手架之稱?!凹s定大于配置”也一直是SpringBoot的標簽,那么,SpringBoot要實現自身優勢,自動配置功不可沒。
一、SpringBoot自動配置是什么
SpringBoot自動配置是指在應用程序啟動時,SpringBoot根據classpath路徑下的jar包自動配置應用程序所需的一系列bean和組件,從而減少開發者的配置工作,提高開發效率。文章源自四五設計網-http://www.133122.cn/45484.html
二、@Import注解
在剖析SpringBoot自動配置原理之前,我們先了解一下@Import注解的使用文章源自四五設計網-http://www.133122.cn/45484.html
1. 方式一: .class方式
定義兩個類A、B,并將其加入到Spring IOC容器中:文章源自四五設計網-http://www.133122.cn/45484.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Datapublic class A{????private Integer id=0;????private String name="classA";????public void print(){????????System.out.println(this.name);????}}@Datapublic class B{????private Integer id=1;????private String name="classB";????public void print(){????????System.out.println(this.name);????}} |
創建一個配置類,并使用@Import注解將類A、B添加到 IOC 容器中:文章源自四五設計網-http://www.133122.cn/45484.html
1 2 3 4 | @Import({A.class,B.class})@Configurationpublic class ClassConfig{???????} |
2.方式二:ImportSelector方式
該方法需要定義類來實現ImportSelector接口,并重寫其中的selectImports( )方法,該方法的返回值是需要添加到IOC容器中的類的全限定類名數組:文章源自四五設計網-http://www.133122.cn/45484.html
1 2 3 4 5 6 7 8 | @Datapublic class C{????private Integer id=2;????private String name="classC";????public void print(){????????System.out.println(this.name);????}} |
編寫類來實現ImportSelector接口:文章源自四五設計網-http://www.133122.cn/45484.html
1 2 3 4 5 6 | public class ImportSelectorTest implements ImportSelector{????@Override????public String[] selectImports(AnnotationMetadata annotationMetada){????????return new String[]{"C.class"};????}} |
使用該類:文章源自四五設計網-http://www.133122.cn/45484.html
1 2 3 4 | @Import({ImportSelectorTest.class})@Configurationpublic class ImportSelectorConfig{???????} |
3.方式三:ImportBeanDefinitionRegistrar方式
定義一個類并實現ImportBeanDefinitionRegistrar接口,重寫其中的registerBeanDefinitions( )方法,此方式可以自定義Bean在容器中的名稱:文章源自四五設計網-http://www.133122.cn/45484.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Datapublic class D{????private Integer id=3;????private String name="ClassD";????public void print(){????????System.out.println(this.name);????}}//定義類實現ImPortBeanDefinitionRegistrar接口,重寫其中的registerBeanDefinitions()方法public class ImportBeanDefinitionRegistrarTest{????@Override????public void registerBeanDefninitions(AnnotationMetadata annotationMetadata,BeanDefinitionRegistry registry{????????RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(D.class);????????registry.registerBeanDefinition("自定義名稱",rootBeanDefinition);????}}//使用上面的類進行導入@Import({ImportBeanDefinitionRegistrarTest})@Configurationpublic class ImportBeanDefinitionRegistrarConfig{} |
三、SpringBoot自動配置原理解析
為了容易分析和理解,我們在IDEA中創建一個SpringBoot項目,創建過程省略,直接跳到該項目的主配置類進行分析:文章源自四五設計網-http://www.133122.cn/45484.html
1 2 3 4 5 6 7 8 | @SpringBootApplicationpublic class SpringBootTestApplication {????public static void main(String[] args) {????????SpringApplication.run(SpringBootTestApplication.class, args);????}} |
其中@SpringBootApplication注解是SpringBoot項目的重點,按住ctrl鍵進入其中,看到它由以下部分組成:文章源自四五設計網-http://www.133122.cn/45484.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan(????excludeFilters = {@Filter(????type = FilterType.CUSTOM,????classes = {TypeExcludeFilter.class}), @Filter(????type = FilterType.CUSTOM,????classes = {AutoConfigurationExcludeFilter.class})})public @interface SpringBootApplication {????//內容省略} |
我們主要關注以下幾個注解:
- @SpringBootConfiguration:標記當前類為配置類
- @EnableAutoConfiuration:開啟自動配置
- @ComponentScan:掃描主類所在包及其子包、同級包中的Bean
1、@SpringBootConfiguration注解:標記當前類為配置類
1 2 3 4 5 6 7 8 9 10 11 | @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Configuration@Indexedpublic @interface SpringBootConfiguration {????@AliasFor(????????annotation = Configuration.class????)????boolean proxyBeanMethods() default true;} |
根據其源碼可以知道,@SpringBootConfiguration注解包含@Configuration,所以其擁有@Configuration注解相似的功能,而@Configuration注解又包含@Companent注解,所以配置類也存在于IOC容器中。
2、@EnableAutoConfiguration注解:開啟自動配置
1 2 3 4 5 6 7 8 9 10 11 12 13 | @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration {????String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";????Class<?>[] exclude() default {};????String[] excludeName() default {};} |
根據其源碼得出其主要由@AutoConfigurationPackages注解和@Import注解組成
@AutoConfigurationPackages注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import({Registrar.class})public @interface AutoConfigurationPackage {????String[] basePackages() default {};????Class<?>[] basePackageClasses() default {};}//其中的@Import注解導入了Registrar類,該類是AutoConfigurationPackages的靜態類部類static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {????????Registrar() {????????}????????/**????????????根據傳入的元注解信息獲取所在的包,將包中組件類封裝為數組進行注冊????????*/????????public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {????????????AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));????????}????????public Set<Object> determineImports(AnnotationMetadata metadata) {????????????return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));????????}????} |
@Import({AutoConfigurationImportSelector.class})注解
這一步就用到了@Import注解使用方式中的第二中:實現ImportSelector接口
那么AutoConfigurationImportSelector接口何時如何被執行呢?
SpringBoot 啟動時會使用 ConfigurationClassParser 來解析被 @Configuration 標識的配置類, 然后再處理這個類內部被其他注解修飾的情況, 比如 @Import 注解, @ComponentScan 注解,@Bean 注解等
若發現注解中存在 @Import(ImportSelector) 的情況下,就會創建一個相應的 ImportSelector 對象,并調用其 process 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {????????????Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {????????????????return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());????????????});????????????AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);????????????this.autoConfigurationEntries.add(autoConfigurationEntry);????????????Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();????????????while(var4.hasNext()) {????????????????String importClassName = (String)var4.next();????????????????this.entries.putIfAbsent(importClassName, annotationMetadata);????????????}????????} |
process方法又調用了getAutoConfigurationEntry方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(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.getConfigurationClassFilter().filter(configurations);????????????this.fireAutoConfigurationImportEvents(configurations, exclusions);????????????return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);????????}????} |
getAutoConfigurationEntry方法調用了getCandidateConfigurations方法:
1 2 3 4 5 | 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;????} |
getCandidateConfigurations方法中使用了Spring Cor中的類SpringFactoriesLoader,該類的loadFactoryNames方法可以根據接口獲取接口類的名稱,這個方法返回的是類名的列表,loadFactoryNames方法會遍歷整個springboot項目的classpath下的ClassLoader中所有jar包下的spring.factories文件。至此自動配置結束
總結
SpringBoot自動配置是SpringBoot的核心,所以了解SpringBoot的自動配置是非常有必要的,大家可以自行查找資料解釋以下為什么不使用@ComponentScan注解替換@Import注解來進行類的導入



評論