SpringBoot | 3.1 配置數據源



前言

前面介紹了SpringBoot的自動配置原理,用一句話概括是:啟動時加載所有,最終按照條件進行裝配。本章節表面上是講數據訪問,但其核心還是講SpringBoot的自動配置,只不過自動配置的對象是數據庫相關的依賴(如:druid、MyBatis、MyBatis-Plugs等)。這些依賴的導入與裝配都是SpringBoot幫我們自動完成的。

SpringBoot默認使用的數據源是Hikari(下面有源碼分析),以后我們將使用阿里的Druid數據源進行數據庫相關配置與操作。

在本篇,我們可以知道:

  • 數據源的自動配置原理(2.);
  • SpringBoot整合第三方技術的兩種方式(3.);
  • SpringBoot在處理自動配置時的邏輯(3.2.2);
  • Druid數據源自動配置原理(3.2.2);

可以直接跳轉至第四點總結那查看源碼結構圖。


1. 數據源的自動配置

我們先基於SpringBoot默認的HikariDataSource數據源,導入JDBC場景,看看SpringBoot幫我們自動配置了什么。

首先導入JDBC場景依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

導入JDBC場景依賴后,我們可以在Maven的Dependencies依賴里看出spring-boot-starter-data-jdbc自動幫我們引入了數據源、JDBC與事務相關jar包。

JDBC場景
少了數據庫連接驅動依賴

我們可以發現,這其中沒有數據庫連接驅動依賴,道理很簡單,SpringBoot並不知道我們要使用什么數據庫(MySQL還是Oracle或其他)。

直接在pom.xml文件里添加數據庫驅動依賴即可

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

我們不用關注數據庫驅動的版本,在SpringBoot里使用版本仲裁自動匹配( 數據庫版本和驅動版本對應 ),當然,我們也可以自定義版本。

自定義連接驅動版本:

  1. pom.xmldependency直接依賴引入具體版本(maven的就近依賴原則)

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.49</version>
    </dependency>
    
  2. pom.xmlproperties重新聲明版本(maven屬性的就近優先原則)

    <properties>
        <java.version>1.8</java.version>
        <mysql.version>5.1.49</mysql.version>
    </properties>
    

2. *數據源自動配置源碼分析

基於上面的例子,我們來剖析SpringBoot數據源的自動裝配原理。

由於數據源的配置是SpringBoot幫我們自動配置的,因此我們在外部依賴庫里找到jdbc相關的自動配置:
在這里插入圖片描述
往下翻翻找到jdbc的包:

在這里插入圖片描述
可以看到引入jdbc相關依賴后SpringBoot幫我們引入了很多自動配置類,如:

  • DataSourceAutoConfiguration:數據源的自動配置類;

  • DataSourceTransactionManagerAutoConfiguration:事務管理器的自動配置;

  • JdbcTemplateAutoConfiguration:JdbcTemplate的自動配置,可以來對數據庫進行crud(JdbcTemplate是Spring對JDBC的封裝,目的是使JDBC更加易於使用);

  • JndiDataSourceAutoConfiguration:jndi的自動配置;

  • XADataSourceAutoConfiguration:分布式事務相關的自動配置;

  • 等......

我們對其中兩個進行分析:

2.1 DataSourceAutoConfiguration:數據源自動配置類

解釋了SpringBoot底層的數據源是Hikari;

其中DataSourceAutoConfiguration是數據源的自動配置類,我們點進去看源碼,發現其定義了一些靜態方法,其中底層數據源相關的是:

@Configuration(proxyBeanMethods = false)
@Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
@Import({Hikari.class, Tomcat.class, Dbcp2.class, OracleUcp.class, Generic.class, DataSourceJmxConfiguration.class})
protected static class PooledDataSourceConfiguration {
    protected PooledDataSourceConfiguration() {
    }
}

其中老熟人@ConditionalOnMissingBean注解的含義是當容器內沒有DataSource數據源時,才進行下面的自動配置默認的數據源;而@Import注解則說明了我們要引入的默認數據源是Hikari數據源。也就是說,在我們不做任何處理的情況下,SpringBoot為我們底層配置好的連接池是:HikariDataSource


2.2 JdbcTemplateAutoConfiguration:JdbcTemplate的自動配置類

說明了jdbc配置的語法:包括前綴與用戶可配置的屬性有哪些;
JdbcTemplate是Spring對JDBC的封裝,目的是使JDBC更加易於使用;

JdbcTemplateAutoConfiguration源碼:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({DataSource.class, JdbcTemplate.class})
@ConditionalOnSingleCandidate(DataSource.class)
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({JdbcProperties.class})
@Import({JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class})
public class JdbcTemplateAutoConfiguration {
    public JdbcTemplateAutoConfiguration() {
    }
}

@EnableConfigurationProperties注解可知,自動配置的相關屬性在JdbcProperties.class類里,我們點進去看看:
在這里插入圖片描述
通過源碼我們可以知道以下信息:

  • 可以在application.properties文件里修改jdbc相關屬性,前綴是:spring.jdbc
  • 可配置項有:
    • fetchSize:為jdbc驅動程序提供一個提示,它提示此Statement生成的ResultSet對象需要更多行時應該從數據庫獲取的行數。
    • maxRows:將此Statement 對象生成的所有ResultSet對象可以包含的最大行數限制設置為給定數。
    • queryTimeout:超時設置。
    • 等……

在這里插入圖片描述


2.3 修改數據源的配置項

這里直接給出示例:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    
#    默認配置,可不寫
#    type: com.zaxxer.hikariDataSource

測試類如下:

@Slf4j
@SpringBootTest
class Boot05WebAdminApplicationTests {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    void contextLoads() {

//        jdbcTemplate.queryForObject("select * from account_tbl")
//        jdbcTemplate.queryForList("select * from account_tbl",)
        Long aLong = jdbcTemplate.queryForObject("select count(*) from account_tbl", Long.class);
        log.info("記錄總數:{}",aLong);
    }
}

3. 配置Druid數據源的兩種方式

在實際生產中,我們一般使用阿里的Druid替換默認的Hikari數據源。

Druid官方github地址為:https://github.com/alibaba/druid

在SpringBoot中,整合第三方技術有兩種方式:自定義與starter。在實際生產中一般使用starter,它只需要引入一個xxx-spring-boot-starter依賴就好,能幫我們省去很多配置工作。但我們也可能遇到自定義配置的需求,因此這兩種方法都會提及。


3.1 自定義配置Druid數據源

重點不在這,這里只列出相關步驟即可;

1) 首先引入druid數據源依賴

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.17</version>
</dependency>

2) SpringBoot采用編寫配置類的方式

在config包下新建MyDataSourceConfig類

@Configuration
public class MyDataSourceConfig {

    // 默認的自動配置是判斷容器中沒有才會配@ConditionalOnMissingBean(DataSource.class),即有@Bean后容器自動配置不生效
    @ConfigurationProperties("spring.datasource") //綁定屬性
    @Bean
    public DataSource dataSource() throws SQLException {
        DruidDataSource druidDataSource = new DruidDataSource();

        //在注冊自己的數據源時要給核心屬性賦值,又因為不能寫死在代碼里,故抽取到配置文件里
//        druidDataSource.setUrl();
//        druidDataSource.setUsername();
//        druidDataSource.setPassword();
        
        //加入監控功能(可寫進配置文件)
        druidDataSource.setFilters("stat,wall");
        //設置最大活躍線程數(可寫進配置文件)
        druidDataSource.setMaxActive(10);
        return druidDataSource;
    }

    /**
     * 配置 druid的監控頁功能
     * @return
     */
    @Bean
    public ServletRegistrationBean statViewServlet(){
        StatViewServlet statViewServlet = new StatViewServlet();
        ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>(statViewServlet, "/druid/*");

        //配置初始化參數:需要賬號密碼才能登陸查看監控頁面
        registrationBean.addInitParameter("loginUsername","admin");
        registrationBean.addInitParameter("loginPassword","123456");

        return registrationBean;
    }

    /**
     * WebStatFilter 用於采集web-jdbc關聯監控的數據
     */
    @Bean
    public FilterRegistrationBean webStatFilter(){
        WebStatFilter webStatFilter = new WebStatFilter();
        FilterRegistrationBean<WebStatFilter> filterRegistrationBean = new FilterRegistrationBean<>(webStatFilter);
        //設置攔截路徑
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/*"));
        filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");

        return filterRegistrationBean;
    }

}

優化寫法:凡是setxxx方法都可以在配置文件中說明,優化后可在配置文件中直接說明。Spring原生配置方式:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
    destroy-method="close">
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
    <property name="maxActive" value="20" />
    <property name="initialSize" value="1" />
    <property name="maxWait" value="60000" />
    <property name="minIdle" value="1" />
    <property name="timeBetweenEvictionRunsMillis" value="60000" />
    <property name="minEvictableIdleTimeMillis" value="300000" />
    <property name="testWhileIdle" value="true" />
    <property name="testOnBorrow" value="false" />
    <property name="testOnReturn" value="false" />
    <property name="poolPreparedStatements" value="true" />
    <property name="maxOpenPreparedStatements" value="20" />

3.2 自動配置Druid數據源及源碼分析

在實際生產中,我們大部分時候使用自動配置方式;相比上面繁瑣而復雜的工作,自動配置就簡單很多了,這里先列出相關步驟方法在,再做源碼分析,看看SpringBoot為我們干了些什么。

3.2.1 自動配置Druid數據源

這里也是直接給出示例代碼;

1) 引入druid-starter

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.17</version>
</dependency>

*小知識:第三方提供的Starter統一用xxx-spring-boot-starter;而官方提供的Starter統一用spring-boot-starter-xxx。

2) 直接配置即可

spring:
  datasource:  #數據源的基本屬性
    url: jdbc:mysql://localhost:3306/db_account
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver

    druid:  #druid數據源相關配置
      aop-patterns: com.atguigu.admin.*  #監控SpringBean
      filters: stat,wall     # 底層開啟功能,stat(sql監控),wall(防火牆)

      stat-view-servlet:   # 配置監控頁功能
        enabled: true  #默認false,需要手動開啟
        login-username: admin  #登錄的用戶名
        login-password: admin  #登錄的密碼
        resetEnable: false  #禁用重置按鈕

      web-stat-filter:  # 監控web
        enabled: true
        urlPattern: /*  #匹配的路徑
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'  #排除的路徑

      filter:
        stat:    # 對上面filters里面的stat的詳細配置
          enabled: true
          slow-sql-millis: 1000  #慢查詢時間,超過1000ms的查詢都是慢查詢
          logSlowSql: true  #是否使用日志記錄慢查詢
        wall:    # 對上面filters里面的wall的詳細配置
          enabled: true
          config:
            drop-table-allow: false

SpringBoot配置官方示例:https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter

配置項列表:
https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE%E5%B1%9E%E6%80%A7%E5%88%97%E8%A1%A8


3.2.2 *自動配置的源碼分析

按上面操作就能把jdbc配置好了,但我們需要知道兩個問題:
1.為什么引入一個依賴就能將jdbc配置好,換句話說引入這個依賴后SpringBoot幫我們干了些什么?
2.用戶可修改的配置項有哪些?
這些問題可以在下面的源碼分析中找到答案。


【核心原理】原理概述:

  • 導入starter場景啟動器后,根據SpringBoot的設置模式,首先找到META-INF包下的spring.factories工廠,通過讀取EnableAutoConfiguration獲取啟動時加載的類 :XXXAutoConfiguration自動配置類;

  • 自動配置類會利用@Bean注解把場景下相關組件注冊進容器中,這些組件的核心配置項會通過@EnableConfigurationProperties注解跟XXXProperties配置文件綁定;

  • 由此我們可以獲得配置類(XXXAutoConfiguration)與配置項(XXXProperties)信息。

    • 配置類(XXXAutoConfiguration)里配置了核心組件;

    • 配置項(XXXProperties)里主要包含兩個信息。其一是通過@ConfigurationProperties注解可以獲取配置文件的前綴(prefix=Constants.XXX);其二是配置項可修改的參數(YYY)名稱及參數(ZZZ)。我們在yml里通過[前綴.參數名稱=參數](XXX.YYY=ZZZ)修改默認參數;


【案例分析】Druid數據源自動配置的實現:

上述原理在Druid數據源自動配置中簡而言之就是:導入starter場景啟動器,根據SpringBoot的設置模式,會有DruidDataSourceAutoConfigure自動配置類,自動配置類會把場景下相關組件注冊進容器中,相關組件的核心配置項跟配置文件綁定。

引入Druid的stater依賴后,可以在META-INF包下的spring.factories工廠里找到Druid數據源的自動配置類:
在這里插入圖片描述
按住“Ctrl+左鍵”點進去查看DruidDataSourceAutoConfigure 源碼:

@Configuration
@ConditionalOnClass({DruidDataSource.class})
@AutoConfigureBefore({DataSourceAutoConfiguration.class})
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
    private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);

    public DruidDataSourceAutoConfigure() {
    }

    @Bean(initMethod = "init")
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        LOGGER.info("Init DruidDataSource");
        return new DruidDataSourceWrapper();
    }
}

這里有幾個需要注意的點:

  • @AutoConfigureBefore(DataSourseAutoConfiguration.class)語句的含義是:在SpringBoot自動配置數據源前先配置Druid數據源;這是一個優先級的關系,即Druid場景的自動配置類優先執行;

    • 原因:官方DataSourseAutoConfiguration里配置了Hikari數據源,只有在容器里沒有數據源時才能配置數據源。(詳情見本篇2.1點)
      @ConditionalOnMissingBean
      public DataSource dataSource(){
          return new DruidDataSourceWrapper();
      }
      
  • @Import()注解給我們導入了以下組件:

    • DruidSpringAopConfiguration.class:利用AOP配置SpringBean監控的相關組件;

      • 配置項:spring.datasource.druid.aop-patterns
    • DruidStatViewServletConfiguration.class:監控頁相關配置;

      • 配置項:spring.datasource.druid.stat-view-servlet,默認開啟
    • DruidWebStatFilterConfiguration.class:web監控配置;

      • 配置項:spring.datasource.druid.web-stat-filter:默認開啟
    • DruidFilterConfiguration.class:所有Druid默認filter的配置;

      private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
      private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
      private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
      private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
      private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
      private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
      private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
      private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filterwall";
      
  • 在DataSource的構造方法里,給我們new了一個DruidDataSourceWrapper(Druid數據源包裝器);點進去查看源碼:

    • 可以知道:擴展配置項(綁定屬性)在spring.datasource.druid里;
    • 包裝器主要作用是讀取用戶在application文件里的前綴為spring.datasource.druid.XXX,屬性為XXX的值,並用這些值對Druid數據源進行配置;

DruidDataSourceWrapper源碼:

@ConfigurationProperties("spring.datasource.druid")
class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean {
    @Autowired
    private DataSourceProperties basicProperties;

    DruidDataSourceWrapper() {
    }

    public void afterPropertiesSet() throws Exception {
        if (super.getUsername() == null) {
            super.setUsername(this.basicProperties.determineUsername());
        }

        if (super.getPassword() == null) {
            super.setPassword(this.basicProperties.determinePassword());
        }

        if (super.getUrl() == null) {
            super.setUrl(this.basicProperties.determineUrl());
        }

        if (super.getDriverClassName() == null) {
            super.setDriverClassName(this.basicProperties.getDriverClassName());
        }
    }

    @Autowired(
        required = false
    )
    public void autoAddFilters(List<Filter> filters) {
        super.filters.addAll(filters);
    }

    public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {
        try {
            super.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
        } catch (IllegalArgumentException var4) {
            super.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
        }

    }
}


4. 總結

在本篇里我們知道了數據源的自動配置原理,其原理結構圖如下:

  • org.springframework.boot.autoconfigure.jdbc:SpringBoot自動配置包
    • DataSourceAutoConfiguration:數據源的自動配置類;
    • DataSourceTransactionManagerAutoConfiguration:事務管理器的自動配置;
    • JdbcTemplateAutoConfiguration:JdbcTemplate的自動配置;
    • JndiDataSourceAutoConfiguration:jndi的自動配置;
    • XADataSourceAutoConfiguration:分布式事務相關的自動配置;

以及Druid數據源自動配置原理,其源碼結構圖如下:

  • DruidDataSourceAutoConfigure:Druid數據源自動配置類;
    • @AutoConfigureBefore():優先配置Druid數據源;
    • @Import():導入組件;
      • DruidSpringAopConfiguration.class:利用AOP配置SpringBean監控的相關組件;
      • DruidStatViewServletConfiguration.class:監控頁相關配置;
      • DruidWebStatFilterConfiguration.class:web監控配置;
      • DruidFilterConfiguration.class:所有Druid默認filter的配置;
    • DruidDataSourceWrapper:Druid數據源包裝器;

最重要的是知道了SpringBoot整合第三方技術的兩種方式:

  • 自定義;
  • starter(推薦);

對SpringBoot的自動配置邏輯也有一定的認識:

  • 導入starter場景啟動器后,根據SpringBoot的設置模式,首先找到META-INF包下的spring.factories工廠,通過讀取EnableAutoConfiguration獲取啟動時加載的類 :XXXAutoConfiguration自動配置類;

  • 自動配置類會利用@Bean注解把場景下相關組件注冊進容器中,這些組件的核心配置項會通過@EnableConfigurationProperties注解跟XXXProperties配置文件綁定;

  • 由此我們可以獲得配置類(XXXAutoConfiguration)與配置項(XXXProperties)信息。

    • 配置類(XXXAutoConfiguration)里配置了核心組件;
    • 配置項(XXXProperties)里主要包含兩個信息。其一是通過@ConfigurationProperties注解可以獲取配置文件的前綴(prefix=Constants.XXX);其二是配置項可修改的參數(YYY)名稱及參數(ZZZ)。我們在yml里通過[前綴.參數名稱=參數](XXX.YYY=ZZZ)修改默認參數;


最后

新人制作,如有錯誤,歡迎指出,感激不盡!
歡迎關注公眾號,會分享一些更日常的東西!
如需轉載,請標注出處!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM