SpringBoot自動配置註解原理解析

SpringBoot自動配置註解原理解析

1. SpringBoot啓動主程序類:

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

每次我們直接直接啓動這個啓動類,SpringBoot就啓動成功了,並且幫我們配置了好多自動配置類。

其中最重要是 @SpringBootApplication 這個註解,我們點進去看一下。

2. SpringBootApplication註解:

@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 : Spring Boot的配置類,標註在某個類上,表示這是一個Spring Boot的配置類

  • @EnableAutoConfiguration: 開啓自動配置類,SpringBoot的精華所在。

  • @ComponentScan包掃描

  以前我們需要配置的東西,Spring Boot幫我們自動配置;@EnableAutoConfiguration告訴SpringBoot開啓自動配置功能;這樣自動配置才能生效;

3. EnableAutoConfiguration註解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

 

兩個比較重要的註解:

  • @AutoConfigurationPackage:自動配置包

  • @Import: 導入自動配置的組件

4. AutoConfigurationPackage註解:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata,
                BeanDefinitionRegistry registry) {
            register(registry, new PackageImport(metadata).getPackageName());
        }

它其實是註冊了一個Bean的定義。

new PackageImport(metadata).getPackageName(),它其實返回了當前主程序類的 同級以及子級     的包組件。

以上圖爲例,DemoApplication是和demo包同級,但是demo2這個類是DemoApplication的父級,和example包同級

也就是說,DemoApplication啓動加載的Bean中,並不會加載demo2,這也就是爲什麼,我們要把DemoApplication放在項目的最高級中。

 

5. Import(AutoConfigurationImportSelector.class)註解:

 

可以從圖中看出  AutoConfigurationImportSelector 繼承了 DeferredImportSelector 繼承了 ImportSelector

ImportSelector有一個方法爲:selectImports。

@Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
        List<String> configurations = getCandidateConfigurations(annotationMetadata,
                attributes);
        configurations = removeDuplicates(configurations);
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = filter(configurations, autoConfigurationMetadata);
        fireAutoConfigurationImportEvents(configurations, exclusions);
        return StringUtils.toStringArray(configurations);
    }

 

可以看到第九行,它其實是去加載  public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";外部文件。這個外部文件,有很多自動配置的類。如下:

 

 

6. 如何自定義自己的Bean:

我們以RedisTemplate爲例:

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    public StringRedisTemplate stringRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

}

我們每次在Spring中使用Redis,都會使用到RedisTemplate這個工具類,但是他默認給我們返回的這個工具類,可能不是很符合我們的要求。比如:我們想要開啓事務,或者想要改變它默認的序列化。

這時候該如何去做呢?

根據前面的分析,只要我們在容器中放入一個RedisTemplate Bean即可。

@Bean("redisTemplate")
    public RedisTemplate<Object, Object> myRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        // 修改序列化爲Jackson
        template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
        // 開啓事務
        template.setEnableTransactionSupport(true);
        return template;
    }

我們自己定義我們的RedisTemplate模板,修改序列化,開啓事務等操作。

我們將我們自己的Bean加入到IoC容器中以後,他就會默認的覆蓋掉原來的RedisTemplate,達到定製的效果。

 

我們在以Kafka爲例:

假設我們想要消費的對象不是字符串,而是一個對象呢?比如Person對象,或者其他Object類呢?

1:我們首先去查找KafkaAutoConfiguration(xxxAutoConfiguration),看看是否有關於Serializer屬性的配置

2:假設沒有我們就去KafkaProperties文件查找是否有Serializer的配置

 

 

 

然後直接在application.properties修改默認序列化就好,連Bean都不需要自己重寫。

 類似這種,可以使用Spring提供的Json序列化,也可以自動使用第三方框架提供的序列化,比如Avro, Protobuff等

spring.kafka.producer.key-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer
spring.kafka.consumer.key-deserializer=com.example.common.MyJson
spring.kafka.consumer.value-deserializer=com.example.common.MyJson

後記:

  •    很多時候我們剛開始看一個知識點,不懂迷茫的時候,真的不要慌,可能說明你暫時的知識儲備還不夠理解。
  •    等你經過不斷的學習,不斷的深入以後
  •    突然有一天,你會突然的醒悟
  •    哇!原來他講的是這個意思
  •    可能我們不理解,我們可以去嘗試去看兩遍,三遍,甚至更多遍,突然會有一個時刻,你會醒悟過來的
  •    且行且珍惜,共勉

原文地址: https://www.cnblogs.com/wenbochang/p/9851314.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章