SpringBoot 自动配置数据源原理

SpringBoot 自动配置数据源原理

Spring Boot 2.0 默认使用 com.zaxxer.hikari.HikariDataSource 数据源
Spring Boot 1.5 默认使用 org.apache.tomcat.jdbc.pool.DataSource 作为数据源;

DataSourceConfiguration 源代码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure.jdbc;

import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.context.annotation.Bean;
import org.springframework.util.StringUtils;

abstract class DataSourceConfiguration {
    DataSourceConfiguration() {
    }

    protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
        return properties.initializeDataSourceBuilder().type(type).build();
    }

    //自定义连接池 接口  spring.datasource.type 配置 
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"}
    )
    static class Generic {
        Generic() {
        }

        @Bean
        public DataSource dataSource(DataSourceProperties properties) {
            //创建数据源 initializeDataSourceBuilder DataSourceBuilder 
            return properties.initializeDataSourceBuilder().build();
        }
    }
	//Dbcp2 连接池
    @ConditionalOnClass({BasicDataSource.class})
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"},
        havingValue = "org.apache.commons.dbcp2.BasicDataSource",
        matchIfMissing = true
    )
    static class Dbcp2 {
        Dbcp2() {
        }

        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.dbcp2"
        )
        public BasicDataSource dataSource(DataSourceProperties properties) {
            return (BasicDataSource)DataSourceConfiguration.createDataSource(properties, BasicDataSource.class);
        }
    }

      //2.0 之后默认默认使用 hikari 连接池
    
    @ConditionalOnClass({HikariDataSource.class})
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"},
        havingValue = "com.zaxxer.hikari.HikariDataSource",
        matchIfMissing = true
    )
    static class Hikari {
        Hikari() {
        }

        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.hikari"
        )
        public HikariDataSource dataSource(DataSourceProperties properties) {
            HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);
            if (StringUtils.hasText(properties.getName())) {
                dataSource.setPoolName(properties.getName());
            }

            return dataSource;
        }
    }
	
    //2.0 之后默认不是使用 tomcat  连接池,或者使用tomcat 容器
    //如果导入tomcat jdbc连接池 则使用此连接池,在使用tomcat容器时候 或者导入此包时候
    @ConditionalOnClass({org.apache.tomcat.jdbc.pool.DataSource.class})
    @ConditionalOnMissingBean({DataSource.class})
    //并且配置的配置是  org.apache.tomcat.jdbc.pool.DataSource 会采用tomcat 连接池
    @ConditionalOnProperty(
        name = {"spring.datasource.type"}, //name用来从application.properties中读取某个属性值
        havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
        //缺少该property时是否可以加载。如果为true,没有该property也会正常加载;反之报错  
        // 不管你配不配置 都以 tomcat 连接池作为连接池
        matchIfMissing = true  //默认是false
    )
    static class Tomcat {
        Tomcat() {
        }
		
        //给容器中加数据源
        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.tomcat"
        )
        public org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
            org.apache.tomcat.jdbc.pool.DataSource dataSource = (org.apache.tomcat.jdbc.pool.DataSource)DataSourceConfiguration.createDataSource(properties, org.apache.tomcat.jdbc.pool.DataSource.class);
            DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
            String validationQuery = databaseDriver.getValidationQuery();
            if (validationQuery != null) {
                dataSource.setTestOnBorrow(true);
                dataSource.setValidationQuery(validationQuery);
            }

            return dataSource;
        }
    }
}

如果在类路径没有找到 jar包 则会跑出异常

Field dataSource in com.example.springsession.demo.jpa.StudentController required a bean of type ‘javax.sql.DataSource’ that could not be found.
- Bean method ‘dataSource’ not loaded because @ConditionalOnClass did not find required class ‘org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType’

  • Bean method ‘dataSource’ not loaded because @ConditionalOnClass did not find required classes ‘javax.transaction.TransactionManager’, ‘org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType’

DataSourceConfiguration

配置文件中没有指定数据源时候 会根据注解判断然后选择相应的实例化数据源对象!

则 type 为空。

@ConditionalOnClass({HikariDataSource.class})
@ConditionalOnMissingBean({DataSource.class})  //注解判断是否执行初始化代码,即如果用户已经创建了bean,则相关的初始化代码不再执行
@ConditionalOnProperty(
    name = {"spring.datasource.type"},  //拿配置文件中的type 如果为空返回fale
    havingValue = "com.zaxxer.hikari.HikariDataSource",  //type 不为空则去 havingValue 对比 ,相同则ture 否则为false 
    matchIfMissing = true  // 不管上面文件中是否配置,默认都进行加载  , matchIfMissing的默值为false
)
static class Hikari {
    Hikari() {
    }

    @Bean
    @ConfigurationProperties(
        prefix = "spring.datasource.hikari"
    )
    public HikariDataSource dataSource(DataSourceProperties properties) {
    	//创建数据源
        HikariDataSource dataSource = 
            (HikariDataSource)DataSourceConfiguration.createDataSource(properties, 
           //创建数据源
HikariDataSource.class);
        if (StringUtils.hasText(properties.getName())) {
            dataSource.setPoolName(properties.getName());
        }

        return dataSource;
    }
}

createDataSource 方法

protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
// 使用DataSourceBuilder 建造数据源,利用反射创建type数据源,然后绑定相关属性
    return properties.initializeDataSourceBuilder().type(type).build();
}

DataSourceBuilder 类

设置type

public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) {
    this.type = type;
    return this;
}

根据设置type的选择类型

private Class<? extends DataSource> getType() {
	//如果没有配置type 则为空 默认选择 findType 
    Class<? extends DataSource> type = this.type != null ? this.type : findType(this.classLoader);
    if (type != null) {
        return type;
    } else {
        throw new IllegalStateException("No supported DataSource type found");
    }
}
public static Class<? extends DataSource> findType(ClassLoader classLoader) {
    String[] var1 = DATA_SOURCE_TYPE_NAMES;
    int var2 = var1.length;
    int var3 = 0;

    while(var3 < var2) {
        String name = var1[var3];

        try {
            return ClassUtils.forName(name, classLoader);
        } catch (Exception var6) {
            ++var3;
        }
    }

    return null;
}

//数组

private static final String[] DATA_SOURCE_TYPE_NAMES = new String[]{"com.zaxxer.hikari.HikariDataSource", "org.apache.tomcat.jdbc.pool.DataSource", "org.apache.commons.dbcp2.BasicDataSource"};

使用tomcat 连接池

spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource

若使用MySQL有个默认机制是八个小时空闲连接会自动回收,这个回收是在MySQL的Server端,而Spring Boot 的连接池不知道这个连接已经不可用了,继续用这个连接查询的话就会报错,在配置数据库连接池的时候增加一些检测配置,把一些不可用的连接释放掉。

添加依赖即可


<dependency>
    <groupId>org.apache.tomcatgroupId>
    <artifactId>tomcat-jdbcartifactId>
    <version>8.5.23version>
dependency>

hikari 数据源一些配置:

#spring.datasource.type=com.zaxxer.hikari.HikariDataSource
#spring.datasource.hikari.minimum-idle=5
#spring.datasource.hikari.maximum-pool-size=15
#spring.datasource.hikari.auto-commit=true
#spring.datasource.hikari.idle-timeout=30000
#spring.datasource.hikari.pool-name=DatebookHikariCP
#spring.datasource.hikari.max-lifetime=1800000
#spring.datasource.hikari.connection-timeout=30000
#spring.datasource.hikari.connection-test-query=SELECT 1

一些常用的HiKariCP的配置参数

dataSourceClassName 这是JDBC驱动程序提供的DataSource类的名称。 请参阅特定JDBC驱动程序的文档以获取此类名.注意不支持XA数据源。 XA需要像bitronix这样的真正的事务管理器。 请注意,如果您使用jdbcUrl进行“old-school”基于DriverManager的JDBC驱动程序配置,则不需要此属性
jdbcUrl 数据库连接url
username 用户名
password 密码
autoCommit 自动提交,默认是true
connectionTimeout 连接超时时间设置,默认是30s
idleTimeout 最大空闲时间,超过最大空闲时间的连接在达到最小空闲连接数 minimumIdle 就不会被回收。默认十分钟
minimumIdle 允许的最小空闲数。当minimumIdl大于等于 maximumPoolSize 时,空闲超时的连接不会被回收。默认是等于 maximumPoolSize
maximumPoolSize 最大连接数,默认10
connectionTestQuery 测试连接是否可用的query语句
maxLifetime 最长生命周期,使用中的连接永远不会退役,只有当它关闭时才会被删除。默认30分钟

注解说明
@ConditionalOnProperty来控制Configuration是否生效

@ConditionalOnMissingBean({xxxx.class}) //注解判断是否执行初始化代码,即如果用户已经创建了bean,则相关的初始化代码不再执行

你可能感兴趣的:(SpringBoot 自动配置数据源原理)