spring-boot怎樣自動配置數據源

spring-boot-autoconfigure
展開
       初次接觸spring-boot的時候,我們經常會看到這樣的文章:“爲什麼要使用spring-boot” "spring-boot與spring mvc有什麼區別",在這些文章中幾乎都會出現這樣的一句話“約定優於配置”,確實是這樣的,spring-boot與spring-mvc的一個重要區別就是spring-boot遵循“約定優於配置”這一原則,而spring-boot-autoconfigure模塊正是完美的實現這個原則,所以我們今天就來說一說spring-boot-autoconfigure這個模塊。

       顧名思義,autoconfigure就是自動配置的意思,我們先看一個比較常用的例子,我們都知道,對於90%的web項目我們都需要使用數據庫(比如mysql),所以就需要用到datasource,我們就先以datasource的自動配置爲例吧。

       使用過spring-boot的都知道,我們只需要在application.yml中進行以下配置即可在應用中使用對應的數據庫連接

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: "jdbc:mysql://127.0.0.1/crs_db?useUnicode=true&characterEncoding=utf"
    username: root
    password: 123456
        對於習慣了spring-mvc的同學來說,這真是太簡潔了!我們下面來看一下spring-boot-autoconfigure是怎樣簡化這些步驟的,話不多說,直奔關鍵代碼: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

 
@Configuration                 // 備註1 
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })  //備註2
@EnableConfigurationProperties(DataSourceProperties.class)  //備註3
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
        DataSourceInitializationConfiguration.class })   //備註4
public class DataSourceAutoConfiguration {
 
    // 這裏是爲了開啓一個內嵌的datasource,因爲不滿足某些條件,因此最終不會實例化一個dataSource
    @Configuration
    @Conditional(EmbeddedDatabaseCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import(EmbeddedDataSourceConfiguration.class)
    protected static class EmbeddedDatabaseConfiguration {
 
    }
 
    
    @Configuration
    @Conditional(PooledDataSourceCondition.class)
    // 由於上面的EmbeddedDatabase沒有實例化成功,所以在這裏還沒有dataSource,所以還可以往下玩
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
            DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
            DataSourceJmxConfiguration.class })       // 備註5
    protected static class PooledDataSourceConfiguration {
 
    }
 
  // 其它代碼省略
     備註1: @Configuration 這個註解我就不解釋了,用過spring-boot的都知道是幹啥的

     備註2:    @ConditionalOnClass表示只有classpath中能找到DataSource.class和EmbeddedDatabaseType.class時,DataSourceAutoConfiguration這個類纔會被spring容器實例化,這其實也解釋了爲什麼我們有時候只需要在pom.xml添加某些依賴包,某些功能就自動打開了,就是這個註解乾的好事兒。

      備註3: @EnableConfigurationProperties引入了DataSourceProperties的配置,簡單看一下代碼,是不是有一種似曾相識的感覺, spring.datasource.url這些不就是application.yml中的配置嗎,所以從這裏我們就知道我們的配置是用在這裏的。

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
 
    private ClassLoader classLoader;
 
    private String name;
 
    private boolean generateUniqueName;
 
    private Class<? extends DataSource> type;
 
    private String driverClassName;
 
    private String url;
 
    private String username;
 
    private String password;
 
    private String jndiName;
 
    private DataSourceInitializationMode initializationMode = DataSourceInitializationMode.EMBEDDED;
 
    private String platform = "all";
 
    private List<String> schema;
 
    private String schemaUsername;
 
    private String schemaPassword;
 
    private List<String> data;
 
    private String dataUsername;
 
    private String dataPassword;
 
    private boolean continueOnError = false;
 
    private String separator = ";";
 
    private Charset sqlScriptEncoding;
 
            備註4: 使用了 @Import註解引入了DataSourcePoolMetadataProvidersConfiguration 和DataSourceInitializationConfiguration,這裏我們重點關注一下DataSourceInitializationConfiguration

@Configuration
public class DataSourcePoolMetadataProvidersConfiguration {
 
       // tomcat自帶的datasource連接池,由於默認情況下spring-boot未引入org.apache.tomcat.jdbc.pool.DataSource對應的依賴包,
       // 根據@ConditionalOnClass註解的作用,這個配置不會生效
    @Configuration
    @ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
    static class TomcatDataSourcePoolMetadataProviderConfiguration {
 
        @Bean
        public DataSourcePoolMetadataProvider tomcatPoolDataSourceMetadataProvider() {
            return (dataSource) -> {
                if (dataSource instanceof org.apache.tomcat.jdbc.pool.DataSource) {
                    return new TomcatDataSourcePoolMetadata(
                            (org.apache.tomcat.jdbc.pool.DataSource) dataSource);
                }
                return null;
            };
        }
 
    }
    
     // hikaricp是spring-boot默認的連接池(據說速度超快),HikariDataSource的包默認在spring-boot的全家桶中,
    //所以最終會實例化DataSourcePoolMetadataProvider 的一個bean
    @Configuration
    @ConditionalOnClass(HikariDataSource.class)
    static class HikariPoolDataSourceMetadataProviderConfiguration {
 
        @Bean
        public DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
            return (dataSource) -> {
                if (dataSource instanceof HikariDataSource) {
                    return new HikariDataSourcePoolMetadata(
                            (HikariDataSource) dataSource);
                }
                return null;
            };
        }
 
    }
 
    // dbcp2連接池的配置,很不幸,BasicDataSource不在默認包中,所以這個連接池不會生效
    @Configuration
    @ConditionalOnClass(BasicDataSource.class)
    static class CommonsDbcp2PoolDataSourceMetadataProviderConfiguration {
 
        @Bean
        public DataSourcePoolMetadataProvider commonsDbcp2PoolDataSourceMetadataProvider() {
            return (dataSource) -> {
                if (dataSource instanceof BasicDataSource) {
                    return new CommonsDbcp2DataSourcePoolMetadata(
                            (BasicDataSource) dataSource);
                }
                return null;
            };
        }
 
    }
 
}
      從上面的代碼可以知道spring-boot默認實現了hikari連接池。

      備註5: @Import引入了DataSourceConfiguration的幾個內部類(Hikari、Tomcat、Dbcp2、Generic) ,上面我們也說了Hikari是親兒子,所以Tomcat和Dbcp2肯定是死在了@ConditionalOnClass上了,所以我們只看Hikari和Generic

     

abstract class DataSourceConfiguration {
 
     //HikariDataSource肯定存在,往下走
       @ConditionalOnClass(HikariDataSource.class)  
     // 在此之前還沒有實例化DataSource,往下走(如果我們在應用中主動實現了datasource,那默認的hikari就不會實現,
     //這個也很常見,比如我們使用阿里巴巴的durid連接池,也是基於類似的原理,當然比這要稍微複雜些)
   @ConditionalOnMissingBean(DataSource.class) 
     // 當配置 spring.datasource.type=com.zaxxer.hikari.HikariDataSource或該配置不存在時,往下走
    @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)
    static class Hikari {
 
        // 若以上4個註解的條件都滿足,則在這裏實例化一個HikariDataSource的bean,很顯然它也是DataSource的子類
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.hikari")
        public HikariDataSource dataSource(DataSourceProperties properties) {
            HikariDataSource dataSource = createDataSource(properties,
                    HikariDataSource.class);
            if (StringUtils.hasText(properties.getName())) {
                dataSource.setPoolName(properties.getName());
            }
            return dataSource;
        }
 
    }
 
    // 若DataSource沒有bean存在,則往下走,很不幸,在上面已經實例化過HikariDataSource,止步於此,
    // 看去來這只是爲了避免之前都沒有實例化dataSource,這裏來默認一個dataSource
    @ConditionalOnMissingBean(DataSource.class)
    @ConditionalOnProperty(name = "spring.datasource.type")
    static class Generic {
 
        @Bean
        public DataSource dataSource(DataSourceProperties properties) {
            return properties.initializeDataSourceBuilder().build();
        }
 
    }
      至此,數據源和連接池都已實例化完成。講解的非常粗糙,但是這不重要,因爲我們今天的主角可不是dataSource,別忘了我們要講的是spring-boot-autoconfigure,通過上面的例子,其實最關鍵的是幾個註解

      @ConditionalOnClass :   當對應的類存在時則表示滿足條件,一個典型的例子,spring-boot中經常會在你添加了某一個模塊的maven依賴之後,該功能就自動開啓了,比如添加org.flywaydb.flyway-core的依賴包後會默認開啓flyway。

      @Import :  類似於spring-mvc的import標籤

      @ConditionalOnMissingBean:  當對應的bean在beanFactory不存在時表示滿足條件,一般用於只允許類的一個bean存在

      @ConditionalOnBean:   與ConditionalOnMissingBean的作用相反,一般用於依賴的bean存在時纔會實例化

      @ConditionalOnProperty:  當對應的屬性值爲指定值時表示滿足條件

      因此我們在接觸到一個新的模塊後,如果spring-boot會自動配置它,那麼我們可以在spring-boot-autoconfigure模塊下找到對應的包,然後找到以AutoConfiguration結尾的類,比如flyway的FlywayAutoConfiguration,cassandra的CassandraAutoConfiguration等等,然後再配合上面的幾個註解,就能很快了解新模塊的加載過程。

      有些愛思考的同學就要問了,你怎麼知道要去找以AutoConfiguration結尾的類,這個就引入了另一個話題,spring-boot是如何實現自動配置的,它怎麼知道需要加載哪些AutoConfiguration,這個其實是用到了spring-factories機制,關於spring-factories的詳細原理需要單獨寫一篇文章來介紹,這裏只是簡單說一下它在autoconfigure中的應用,我們直接來看一下spring-boot-autoconfigure-2.0.6.RELEASE.jar包下的spring.factories文件

裏面有這樣的一部分配置(如下),key是org.springframework.boot.autoconfigure.EnableAutoConfiguration,value是各個以逗號隔開的*AutoConfiguration,結合spring-factories的運行原理,我們就可以知道所有的自動配置是從這裏開始加載的。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration
       知道了原理後,我們也可以自己定義自己的自動配置模塊,只需要在自己的jar包下添加 MATA-INF/spring.factories文件,然後加上配置org.springframework.boot.autoconfigure.EnableAutoConfiguration=[我們自己寫的autoconfigure],舉個常用的例子,上面我們提到了阿里巴巴的durid數據庫連接池,durid也專門提供了自動配置支持

    其中spring.factories中的內容是這樣的

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
     DruidDataSourceAutoConfigure.java中的內容如下,跟我們之前介紹的dataSource的例子差不多,都是使用了幾個常用的註解來實現自動配置。

     總結一下,spring-boot通過spring-boot-autoconfigure體現了"約定優於配置"這一設計原則,而spring-boot-autoconfigure主要用到了spring.factories和幾個常用的註解條件來實現自動配置,思路很清晰也很簡單。

      但是我特別偏愛spring-boot-autoconfigure這個模塊,其中一個最大的好處就是,當我在使用一個新的功能或模塊時,比如spring內置的flyway,cassandra或是阿里巴巴第三方的durid,我只需要順着spring-boot-autoconfigure的自動配置,就能大致瞭解新模塊的加載過程以及運行的原理,這對於我們熟悉新的模塊確實起到了事半功倍的效果,希望spring-boot-autoconfigure也能給各位帶來更好的學習體驗。
————————————————
版權聲明:本文爲CSDN博主「五克松」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/song_java/article/details/86509971

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