SpringBoot自動配置註解原理解析
1. SpringBoot啟動主程式類:
1 @SpringBootApplication 2 public class DemoApplication { 3public static void main(String[] args) { 4 5SpringApplication.run(DemoApplication.class, args); 6} 7 }
每次我們直接直接啟動這個 啟動類, SpringBoot就啟動成功了,並且幫我們配置了好多自動配置類。
其中最重要是 @SpringBootApplication 這個註解,我們點進去看一下。
2. SpringBootApplication註解:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Inherited 5 @SpringBootConfiguration 6 @EnableAutoConfiguration 7 @ComponentScan(excludeFilters = { 8@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), 9@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) 10 public @interface SpringBootApplication {
三個比較重要的註解:
-
@SpringBootConfiguration : Spring Boot的配置類,標註在某個類上,表示這是一個Spring Boot的配置類
-
@EnableAutoConfiguration: 開啟自動配置類,SpringBoot的精華所在。
-
@ComponentScan包掃描
以前我們需要配置的東西,Spring Boot幫我們自動配置;@EnableAutoConfiguration告訴SpringBoot開啟自動配置功能;這樣自動配置才能生效;
3. EnableAutoConfiguration註解:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Inherited 5 @AutoConfigurationPackage 6 @Import(AutoConfigurationImportSelector.class) 7 public @interface EnableAutoConfiguration {
兩個比較重要的註解:
-
@AutoConfigurationPackage:自動配置包
-
@Import: 匯入自動配置的元件
4. AutoConfigurationPackage註解:
1static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { 2 3@Override 4public void registerBeanDefinitions(AnnotationMetadata metadata, 5BeanDefinitionRegistry registry) { 6register(registry, new PackageImport(metadata).getPackageName()); 7}
它其實是註冊了一個Bean的定義。
new PackageImport(metadata).getPackageName(),它其實返回了當前主程式類的 同級以及子級 的包元件。
以上圖為例, DemoApplication是和demo包同級,但是demo2這個類是DemoApplication的父級,和example包同級
也就是說, DemoApplication啟動載入的Bean中,並不會載入demo2, 這也就是為什麼,我們要把DemoApplication放在專案的最高階中。
5. Import(AutoConfigurationImportSelector.class )註解:
可以從圖中看出 AutoConfigurationImportSelector 繼承了 DeferredImportSelector 繼承了 ImportSelector
ImportSelector有一個方法為: selectImports。
1 @Override 2public String[] selectImports(AnnotationMetadata annotationMetadata) { 3if (!isEnabled(annotationMetadata)) { 4return NO_IMPORTS; 5} 6AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader 7.loadMetadata(this.beanClassLoader); 8AnnotationAttributes attributes = getAttributes(annotationMetadata); 9List<String> configurations = getCandidateConfigurations(annotationMetadata, 10attributes); 11configurations = removeDuplicates(configurations); 12Set<String> exclusions = getExclusions(annotationMetadata, attributes); 13checkExcludedClasses(configurations, exclusions); 14configurations.removeAll(exclusions); 15configurations = filter(configurations, autoConfigurationMetadata); 16fireAutoConfigurationImportEvents(configurations, exclusions); 17return StringUtils.toStringArray(configurations); 18}
可以看到第九行,它其實是去載入 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 外部檔案。這個外部檔案,有很多自動配置的類。如下:
6. 如何自定義自己的Bean:
我們以RedisTemplate為例:
1 @Configuration 2 @ConditionalOnClass(RedisOperations.class) 3 @EnableConfigurationProperties(RedisProperties.class) 4 @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) 5 public class RedisAutoConfiguration { 6 7@Bean 8@ConditionalOnMissingBean(name = "redisTemplate") 9public RedisTemplate<Object, Object> redisTemplate( 10RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { 11RedisTemplate<Object, Object> template = new RedisTemplate<>(); 12template.setConnectionFactory(redisConnectionFactory); 13return template; 14} 15 16@Bean 17@ConditionalOnMissingBean 18public StringRedisTemplate stringRedisTemplate( 19RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { 20StringRedisTemplate template = new StringRedisTemplate(); 21template.setConnectionFactory(redisConnectionFactory); 22return template; 23} 24 25 }
我們每次在Spring中使用Redis,都會使用到RedisTemplate這個工具類,但是他預設給我們返回的這個工具類,可能不是很符合我們的要求。比如: 我們想要開啟事務,或者想要改變它預設的序列化。
這時候該如何去做呢?
根據前面的分析,只要我們在容器中放入一個RedisTemplate Bean即可。
1 @Bean("redisTemplate") 2public RedisTemplate<Object, Object> myRedisTemplate( 3RedisConnectionFactory redisConnectionFactory) { 4RedisTemplate<Object, Object> template = new RedisTemplate<>(); 5template.setConnectionFactory(redisConnectionFactory); 6// 修改序列化為Jackson 7template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer()); 8// 開啟事務 9template.setEnableTransactionSupport(true); 10return template; 11}
我們自己定義我們的RedisTemplate模板,修改序列化,開啟事務等操作。
我們將我們自己的Bean加入到IoC容器中以後,他就會預設的覆蓋掉原來的RedisTemplate,達到定製的效果。
我們在以Kafka為例:
假設我們想要消費的物件不是字串,而是一個物件呢?比如Person物件,或者其他Object類呢?
1:我們首先去查詢 KafkaAutoConfiguration(xxxAutoConfiguration) ,看看是否有關於 Serializer 屬性的配置
2:假設沒有我們就去 KafkaProperties 檔案查詢是否有 Serializer 的配置
然後直接在 application.properties 修改預設序列化就好,連Bean都不需要自己重寫。
類似這種,可以使用Spring提供的Json序列化,也可以自動使用第三方框架提供的序列化,比如Avro, Protobuff等
1 spring.kafka.producer.key-serializer=org.springframework.kafka.support.serializer.JsonSerializer 2 spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer 3 spring.kafka.consumer.key-deserializer=com.example.common.MyJson 4 spring.kafka.consumer.value-deserializer=com.example.common.MyJson
後記:
- 很多時候我們剛開始看一個知識點,不懂迷茫的時候,真的不要慌,可能說明你暫時的知識儲備還不夠理解。
- 等你經過不斷的學習,不斷的深入以後
- 突然有一天,你會突然的醒悟
- 哇!原來他講的是這個意思
- 可能我們不理解,我們可以去嘗試去看兩遍,三遍,甚至更多遍,突然會有一個時刻,你會醒悟過來的
- 且行且珍惜,共勉