关于SpringBoot自动加载DataSource的解析

前言

本次探究的过程是因为在学习Shardingsphere时所引起的,在看Shardingsphere的文档中,发现并没有关于在SpringBoot中集成的介绍,这让我很好奇,有两个原因,其一,我记得之前在项目中是有看到关于shardingsphere-jdbc的starter的,其二,针对大部分项目一般都会提供相应的starter供使用,以便简化开发,比如Druid、Mybatis-Plus等。

带着这两个疑问,我查阅了资料,疑问得到了解决,原来Shardingsphere官方在5.3.0版本之前确实是有提供starter的,后来为了保持yaml配置统一等,不再提供了,可以使用5.1.2版本提供的原生 JDBC 驱动 ShardingSphereDriver

由此引起了我关于SpringBoot自动配置DataSource,及DataSource和Driver原理的兴趣,之前仅是会使用而已,对其原理是一知半解的,借此机会,进行深入的研究。

准备

在Mysql库中建一张表,如下

create table info(
    id int(4) not null auto_increment ,
	name varchar(32) default null,
	age int(2) default null,
    primary key(id)
)engine=innodb;

insert into info(name,age) values('zs',10),('ls',20);

JDBC原生使用回顾

pom中引入mysql依赖

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

java实现

public class JdbcTest {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://192.168.5.100:3306/test?useSSL=false";
        String user = "root";
        String password = "root1234";
        Class.forName(driver);
        try (
                Connection connection = DriverManager.getConnection(url, user, password);
                Statement statement = connection.createStatement();
        ) {
            ResultSet resultSet = statement.executeQuery("select id,name,age from info");
            while (resultSet.next()){
                 String result = String.format("id:%s name:%s age:%s"
                         , resultSet.getInt("id")
                         , resultSet.getString("name")
                         , resultSet.getInt("age"));
                System.out.println(result);
            }
            resultSet.close();
        }
    }
}

SpringBoot自动集成DataSource回顾

pom依赖

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

<!--
这里提前剧透一下,spring-boot-starter-jdbc和其他的starter有些区别,它是一个空包,
并没有任何文件或代码,它的作用是引入了spring-jdbc包和HikariCP包,所以,不引入它,
用spring-jdbc包和HikariCP包代替,效果是一样的
-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

application.properties配置

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.5.100:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root1234

java实现

@Component
public class TestRunner implements ApplicationRunner {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<Map<String, Object>> list = jdbcTemplate.queryForList("select id,name,age from info");
        System.out.println(list);
    }
}

源码分析

上面两个例子应该是我们大家都学习过的,我们知道,上述例子都是去数据库查数据,但两者的区别很大,但是,最终DataSource也是要获得一个Connection然后去查询的,那这两者是怎么串联起来的呢,接下来,我们就从源码开始进行分析。

SpringBoot自动注入

我们知道SpringBoot自动注入就是因为有spring-boot-autoconfigure包,在spring-boot-autoconfigure的META-INF目录下我们可以找到SpringBoot的配置文件org.springframework.boot.autoconfigure.AutoConfiguration.imports,里面可以找到org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

DataSourceAutoConfiguration类就是DataSource自动注入的入口。

aaa

注: 这里我使用的springboot版本是2.7的,在之前的版本DataSourceAutoConfiguration是在spring.factories中配置的。

DataSourceAutoConfiguration类解读

DataSourceAutoConfiguration类上注解

下面我们来解读一下DataSourceAutoConfiguration上的注解

AutoConfiguration

是SpringBoot2.7版本新出现的注解,是为了区分自动配置和普通配置类,其作用几乎等价于Configuration。

ConditionalOnClass

条件注入,表示在项目中必须有DataSourceEmbeddedDatabaseType类才会注入,注意,必须是同时有这两个类才可以(作者在学习时就犯迷糊了,以为引入一个就可以),但是不是必须要注入Spring容器。

DataSource是jdk中自带的类,肯定会有,而EmbeddedDatabaseType类位于spring-jdbc包中,所以,我们必须引入spring-jdbc依赖,才会自动注入,所以在上文我们引入依赖时,可以拆开引用。

ConditionalOnMissingBean

当Spring容器中没有io.r2dbc.spi.ConnectionFactory类型的bean时,才会自动注入,这个本人并没有深入研究,一般的项目应该是没有注入该类型的bean。

EnableConfigurationProperties

自动注入@ConfigurationProperties注解标注的类,将配置的url、username等注入到DataSourceProperties中。

我们在平常使用@ConfigurationProperties注解注入属性时,仅使用该单个注解时其实是并没有生效的,必须搭配@Component等注解才会生效(之前虽然都在使用,我也是才注意到ヾ(≧▽≦*)o)。

Import

向Spring容器注入DataSourcePoolMetadataProvidersConfigurationbean,该步骤暂时不涉及主流程,就不详细分析了。

DataSourceAutoConfiguration类中的DataSourceConfiguration

DataSourceAutoConfiguration类里面引入了两个关于DataSource的配置,第一个是内嵌数据源,第二个是池化数据源。

@Conditional

条件注解,只有一个值value,要求是Condition类型的类,Condition有一个方法matches,返回是否匹配。

其中EmbeddedDatabaseConfiguration类配置的是EmbeddedDatabaseCondition,PooledDataSourceConfiguration配置的是PooledDataSourceCondition。

EmbeddedDatabaseCondition的第一个条件是配置了spring.datasource.url属性就不满足条件,所以EmbeddedDatabaseConfiguration不会被注入。

PooledDataSourceCondition继承了AnyNestedCondition类,AnyNestedCondition表示内部类只要任意一个满足条件即可,因为我们配置了spring.datasource.type属性,所以该条件满足。

@ConditionalOnMissingBean

表示Spring容器中不能有DataSource和XADataSource类型的bean,才会自动注入,这一步保证了我们自动注入的优先级更高,不会出现注入多个DataSource。

因为我们代码中并没有主动注入,所以这一步是满足的,PooledDataSourceConfiguration类上的条件都满足,所以会注入PooledDataSourceConfiguration。

PooledDataSourceConfiguration是一个空类,它是一个空架子,注入它的目的是因为对应的数据源配置。

Spring默认内置了6种类型的数据源,它是根据我们配置的spring.datasource.type属性来注入的。

DataSourceConfiguration.Hikari解析

@ConditionalOnClass表示当存在HikariDataSource类时,@ConditionalOnMissingBean表示当容器中没有DataSource类型的Bean时,需要注意的是第三个注解@ConditionalOnProperty,它表示当spring.datasource.type类型为com.zaxxer.hikari.HikariDataSource时满足条件,可是我们并没有配置datasourceType,它为什么也生效了呢,因为它的第三个属性matchIfMissing,如果没有配置,则默认是满足的(我因为这个问题看了好久🤣)。

总结

至此,我们就明白了,当我们只要简单配置一下,springboot就会帮我们自动注入datasource,在这个过程中,springboot大量使用了条件注解,不得不感概,springboot是真的强大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值