1. 程式人生 > 其它 >9.springboot自動配置原始碼

9.springboot自動配置原始碼

springboot自動載入的原始碼解析:
1.springboot的啟動類程式碼如下:
    //使用@SpringBootApplication標籤標明當前類是springboot的啟動類
    @SpringBootApplication
    public class MainApplication {
        public static void main(String[] args) {
            System.out.println("啟動springboot專案...");
            ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
            System.out.println("啟動成功!");
        }
    }
    
2.仔細研究@SpringBootApplication註解:
    ...
    1.@SpringBootConfiguration
    2.@EnableAutoConfiguration
    @ComponentScan(
        excludeFilters = {@Filter(
        type = FilterType.CUSTOM,
        classes = {TypeExcludeFilter.class}
    ), @Filter(
        type = FilterType.CUSTOM,
        classes = {AutoConfigurationExcludeFilter.class}
    )}
    )
    public @interface SpringBootApplication {
        ...
        //該屬性可以設定掃描哪個包:例如@SpringBootApplication(scanBasePackages = "cn.com")
        String[] scanBasePackages() default {};
    }
    
3.詳解1.@SpringBootConfiguration註解:發現其底層也是@Configuration註解,該註解的作用:告訴springboot當前是配置類,可以配合@bean往容器中載入元件
    ...
    @Configuration
    public @interface SpringBootConfiguration {
        ...
    }
    例如直接在啟動類中使用@bean註冊元件:
        @SpringBootApplication
        public class MainApplication {
            public static void main(String[] args) {
                System.out.println("啟動springboot專案...");
                ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);
                System.out.println("啟動成功!");
            }
            //此方法是可行的,但是一般是使用專門的配置類進行註冊
            @Bean
            public Car getCar() {
                System.out.println("car的建立方法!");
                return new Car("寶馬", "14萬");
            }
        }
        
4.詳解2註解:@EnableAutoConfiguration自動配置,這是springboot的核心:
    4.1.@EnableAutoConfiguration原始碼
        ....
        @AutoConfigurationPackage
        @Import({AutoConfigurationImportSelector.class})
        public @interface EnableAutoConfiguration {
            ...
        }
        4.1.2研究其下面註解:@AutoConfigurationPackage的內容
            //往容器中匯入Registrar類
            @Import({Registrar.class})
            public @interface AutoConfigurationPackage {
                ...
            }
            Registrar類的詳情如下:
            static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
                //註冊元件
                public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
                    AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
                }
            }
4.1.3 @Import({AutoConfigurationImportSelector.class})程式碼研究(自動匯入各種選擇器)
        AutoConfigurationImportSelector類程式碼:
            public String[] selectImports(AnnotationMetadata annotationMetadata) {
                ...
                AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
                 ...
             }
        下續程式碼:
            protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
                   ...
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                  ...
            }
        下續程式碼:
            最終發現:所有的選擇器匯入的bean都在spring-boot-autoconfigure.jar/META-INF/spring.factories中配置:約有127項
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");
按需開啟自動配置項:
雖然127個場景所有的自動配置會在springboot啟動時全部載入,
最終會進行按需配置,容器中不會存在127個所有的元件,會按需配置:
例子:
   例如aop類中:如果專案中有Advice.class這個類,會進行aop bean的註冊,但是專案並沒有匯入Advice的包,自然不會有該類,也不會配置該場景的啟動器
       //條件選擇器:當容器中有這個類時執行下述方法
        @ConditionalOnClass({Advice.class})
        static class AspectJAutoProxyingConfiguration {
        }
    例如:amqp類中:
        //條件選擇器:當容器中存在RabbitTemplate這個類時,會執行下述方法
        @ConditionalOnClass({RabbitTemplate.class, Channel.class})
        @EnableConfigurationProperties({RabbitProperties.class})
        @Import({RabbitAnnotationDrivenConfiguration.class})
        public class RabbitAutoConfiguration {
        }
注意,注意,注意..... 自動配置的類後面都是XXXAutoConfiguration
每個場景的自動配置類都會將配置關聯到一個配置類上(使用@EnableConfigurationProperties(類名)標籤去關聯springboot的配置檔案和這個類)
示例如下:
    1.HttpEncodingAutoConfiguration自動配置類
            //1.標明當前類是springboot配置類
            @Configuration(
                proxyBeanMethods = false
            )
            //2.當前自動配置類和ServerProperties類進行繫結
            @EnableConfigurationProperties({ServerProperties.class})
            //3.當前應用是傳統的servlet的web工程
            @ConditionalOnWebApplication(
                type = Type.SERVLET
            )
            //4.專案是否存在CharacterEncodingFilter類
            @ConditionalOnClass({CharacterEncodingFilter.class})
            //5.條件判斷註解:springboot的配置檔案中是否存在erver.servlet.encoding為字首的標籤,matchIfMissing=true,即使不存都生效
            @ConditionalOnProperty(
                prefix = "server.servlet.encoding",
                value = {"enabled"},
                matchIfMissing = true
            )
            public class HttpEncodingAutoConfiguration {
                ...
            }
    
    
    2.繫結的ServerProperties類程式碼如下:
            @ConfigurationProperties(
                //指定配置檔案的字首為:server
                prefix = "server",
                ignoreUnknownFields = true
            )
            public class ServerProperties {
                ...
                private Integer port;
                private InetAddress address;
                ...
            }
    
    3.在springboot的配置檔案中:application.properties中的繫結配置為:
        server.port=80080

原理總結:
        在繫結的配置類中,各屬性有自己預設的值,但當需要更改時,在application.properties中進行配置,@ConfigurationProperties標籤
會將其繫結到配置類上的具體屬性,達到修改預設值的效果!