1. 程式人生 > >SpringBoot學習系列之一:配置自動化

SpringBoot學習系列之一:配置自動化

引言
大家都知道SpringBoot簡化了Spring開發工作,讓開發者不用再去面對繁瑣的配置,可以使我們可以迅速上手進行開發,將重點放在業務邏輯的實現上。但也正因為這樣,使得開發者容易忽略對於其背後原理的理解。我們可能知道怎麼用,但是實際上並不知道SpringBoot如何實現自動配置以及如何通過內建tomcat進行啟動等等的原理。為了探究SpringBoot背後的技術原理,特地將學習的過程記錄下來形成一個文章系列,另外希望對這方面有相同困惑的同學有所裨益。

  • 自動配置介紹
  • Kafka自動配置原始碼分析
  • 總結

一、自動配置介紹
我們都知道,在沒有SpringBoot之前,利用Spring

進行開發的時候,研發需要花費大量精力去定義模板化的各類配置檔案。Spring最初使用Bean Factory以及動態代理實現各模組之間的解耦,它通過配置檔案將bean掃描到Spring容器中。而SpringBoot將這種xml解析配置的過程,通過註解自動配置的方式來進行替換,它根據定義在classpath下的類,自動生成對應的bean,同時將其載入到Springcontext中。SpringBoot通過條件化配置來啟動某個能力項。

在SpringBoot啟動類WebApplication中,可以看到很多個註解。我們知道SpringBoot專案是高度依賴註解的,它可以在main函式中啟動整個應用。

@SpringBootApplication(scanBasePackages = {"com.test"})
@MapperScan("com.test.module.mapper")
@ImportResource(locations = {"classpath:springMVC-servlet.xml"})
@ServletComponentScan
public class WebApplication extends SpringBootServletInitializer{
    static Logger logger = LoggerFactory.getLogger(WebApplication.class);
    public
static void main(String[] args) { SpringApplication.run(WebApplication.class, args); } }

以上程式碼中,@SpringBootApplicationSpringBoot的核心註解,它是一系列註解的集合。它對應的原始碼如下所示。在這些註解當中@EnableAutoConfiguration即為當前的專案提供自動配置功能,它也是一系列註解的集合。該註解可以讓Spring Boot根據類路徑中的jar包依賴為當前專案進行自動配置。

@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 {

    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

}

通過註解的方式實現配置的自動化,主要在spring-boot-autoconfigure-1.4.3.RELEASE-sources.jar這個jar包中提供了對於SpringBoot自動化配置的支援。這個jar包中包含了如下包,篇幅有限只列出了部分包。

這裡寫圖片描述

在這個jar包中的META-INF資料夾中,可以看到spring.factories檔案
這裡寫圖片描述
在spring.factories檔案中我們看到了一些初始化的類、監聽器以及構建類等等。
這裡寫圖片描述

我們具體看一下@EnableAutoConfiguration這個註解裡面的內容。Spring中有很多@Enable-*開頭的註解,類似@EnableScheduling以及@EnableCaching等等,這類註解即為該修飾的類賦予某項能力,在每個該類註解中都會通過@Import註解來匯入實現對應功能的類。

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

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    /**
     * Exclude specific auto-configuration classes such that they will never be applied.
     * @return the classes to exclude
     */
    Class<?>[] exclude() default {};

    /**
     * Exclude specific auto-configuration class names such that they will never be
     * applied.
     * @return the class names to exclude
     * @since 1.3.0
     */
    String[] excludeName() default {};

}

在該註解中引入了EnableAutoConfigurationImportSelector這個類,按照這個類的字面理解為自動配置匯入選擇器,它實現了以下幾個介面。

public class AutoConfigurationImportSelector
        implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
        BeanFactoryAware, EnvironmentAware, Ordered{
......
        }

在這個類中,使用SpringFactoriesLoader.loadFactoryNames方法來掃描具有META-INF/spring.factories檔案的jar包,spring-boot-autoconfigure-x.x.x.x.jar裡就有一個spring.factories檔案,這個檔案中聲明瞭有哪些類要自動配置。

    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
        //獲取    
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                getSpringFactoriesLoaderFactoryClass(), 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;
    }

二、Kafka自動配置原始碼分析
下面分析下Kafka自動配置類,貼上其原始碼,相關原始碼已經加上註釋。

//配置註解
@Configuration
//KafkaTemplate類在classpath目錄下存在時,才會去解析KafkaAutoConfiguration自動配置類
@ConditionalOnClass(KafkaTemplate.class)
//自動注入屬性,如果在application.properties配置檔案中定義,則會將配置檔案中key對應的value值注入到KafkaProperties中
@EnableConfigurationProperties(KafkaProperties.class)
//匯入KafkaAnnotationDrivenConfiguration
@Import(KafkaAnnotationDrivenConfiguration.class)
public class KafkaAutoConfiguration {

    private final KafkaProperties properties;

    private final RecordMessageConverter messageConverter;

    public KafkaAutoConfiguration(KafkaProperties properties,
            ObjectProvider<RecordMessageConverter> messageConverter) {
        this.properties = properties;
        this.messageConverter = messageConverter.getIfUnique();
    }

    //向Spring容器注入bean
    @Bean
    //在上下文中沒有KafkaTemplate時,才會例項化bean
    @ConditionalOnMissingBean(KafkaTemplate.class)
    public KafkaTemplate<?, ?> kafkaTemplate(
            ProducerFactory<Object, Object> kafkaProducerFactory,
            ProducerListener<Object, Object> kafkaProducerListener) {
        KafkaTemplate<Object, Object> kafkaTemplate = new KafkaTemplate<>(
                kafkaProducerFactory);
        if (this.messageConverter != null) {
            kafkaTemplate.setMessageConverter(this.messageConverter);
        }
        kafkaTemplate.setProducerListener(kafkaProducerListener);
        kafkaTemplate.setDefaultTopic(this.properties.getTemplate().getDefaultTopic());
        return kafkaTemplate;
    }

    @Bean
    @ConditionalOnMissingBean(ProducerListener.class)
    public ProducerListener<Object, Object> kafkaProducerListener() {
        return new LoggingProducerListener<>();
    }

    @Bean
    @ConditionalOnMissingBean(ConsumerFactory.class)
    public ConsumerFactory<?, ?> kafkaConsumerFactory() {
        return new DefaultKafkaConsumerFactory<>(
                this.properties.buildConsumerProperties());
    }

    @Bean
    @ConditionalOnMissingBean(ProducerFactory.class)
    public ProducerFactory<?, ?> kafkaProducerFactory() {
        DefaultKafkaProducerFactory<?, ?> factory = new DefaultKafkaProducerFactory<>(
                this.properties.buildProducerProperties());
        String transactionIdPrefix = this.properties.getProducer()
                .getTransactionIdPrefix();
        if (transactionIdPrefix != null) {
            factory.setTransactionIdPrefix(transactionIdPrefix);
        }
        return factory;
    }

    @Bean
    @ConditionalOnProperty(name = "spring.kafka.producer.transaction-id-prefix")
    @ConditionalOnMissingBean
    public KafkaTransactionManager<?, ?> kafkaTransactionManager(
            ProducerFactory<?, ?> producerFactory) {
        return new KafkaTransactionManager<>(producerFactory);
    }

    @Bean
    @ConditionalOnProperty(name = "spring.kafka.jaas.enabled")
    @ConditionalOnMissingBean
    public KafkaJaasLoginModuleInitializer kafkaJaasInitializer() throws IOException {
        KafkaJaasLoginModuleInitializer jaas = new KafkaJaasLoginModuleInitializer();
        Jaas jaasProperties = this.properties.getJaas();
        if (jaasProperties.getControlFlag() != null) {
            jaas.setControlFlag(jaasProperties.getControlFlag());
        }
        if (jaasProperties.getLoginModule() != null) {
            jaas.setLoginModule(jaasProperties.getLoginModule());
        }
        jaas.setOptions(jaasProperties.getOptions());
        return jaas;
    }

    @Bean
    @ConditionalOnMissingBean
    public KafkaAdmin kafkaAdmin() {
        KafkaAdmin kafkaAdmin = new KafkaAdmin(this.properties.buildAdminProperties());
        kafkaAdmin.setFatalIfBrokerNotAvailable(this.properties.getAdmin().isFailFast());
        return kafkaAdmin;
    }

}

三、總結
將SpringBoot自動配置過程用流程圖進行表示,更能形象化的理解自動配置的流程。

這裡寫圖片描述