欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot学习之路---数据访问&杂谈自动配置原理

程序员文章站 2022-04-19 21:59:58
...

SpringBoot底层与数据库打交道是基于 SpringData来操作的,今天就来简单记录下这其中的奥秘


这篇博客不整合其他的ORM框架,仅仅使用jdbc的方式来探究原理。

首先我们要访问数据库,就需要一个数据源,有关数据源的配置SpringBoot是放在DataSourceConfiguration

我们点进去发现这个类是和全局配置文件中的spring.datasource.type属性相绑定的:

	@ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"}
    )

同时,它下面有几个方法,是依据我们系统引入了哪些包,而选用哪种数据源(SpringBoot2.x默认采用hikari数据源)

	@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"
        )
        BasicDataSource dataSource(DataSourceProperties properties) {
            return (BasicDataSource)DataSourceConfiguration.createDataSource(properties, BasicDataSource.class);
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @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"
        )
        HikariDataSource dataSource(DataSourceProperties properties) {
            HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);
            if (StringUtils.hasText(properties.getName())) {
                dataSource.setPoolName(properties.getName());
            }

            return dataSource;
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({org.apache.tomcat.jdbc.pool.DataSource.class})
    @ConditionalOnMissingBean({DataSource.class})
    @ConditionalOnProperty(
        name = {"spring.datasource.type"},
        havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
        matchIfMissing = true
    )
    static class Tomcat {
        Tomcat() {
        }

        @Bean
        @ConfigurationProperties(
            prefix = "spring.datasource.tomcat"
        )
        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;
        }
    }

现在我们就去全局配置中来配置,以yaml文件为例

spring:
  datasource:
    username: root
    password: ***
    url: jdbc:mysql://localhost:3306/demo
    driver-class-name: com.mysql.jdbc.Driver

这样就配置好了,并且它会帮我们自动注入到容器中。有数据源后,我们就来测试一下,写个测试方法,打印出数据源和连接:

@SpringBootTest
class SpringbootDataApplicationTests {

    @Autowired
    private DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
        System.out.println("i am datasource:"+dataSource.getClass().getName());
        Connection connection = dataSource.getConnection();
        System.out.println(connection);

    }

}

运行结果
SpringBoot学习之路---数据访问&杂谈自动配置原理
获取连接成功

我们目光回到DatasourceConfiguration中,如果我们想用到自己指定的数据源,它是怎么样的

/**
 * Generic DataSource configuration.
 */
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {

   @Bean
   public DataSource dataSource(DataSourceProperties properties) {
       //使用DataSourceBuilder创建数据源,利用反射创建响应type的数据源,并且绑定相关属性
      return properties.initializeDataSourceBuilder().build();
   }

}

数据源这块的源码解析完毕,接着看看还有一个类DataSourceInitializer,它就是帮我们初始化数据源的,内部有两个方法
runSchemaScriptsrunDataScripts,一个是帮我们运行建表语句,另外是运行插入数据库的sql语句

  1. runSchemaScripts:它底层也是根据你指定的sql文件的路径,去运行指定的sql脚本,如果没有指定路径,则默认去寻找schema.sql/schema-all.sql文件并执行。(SpringBoot2.x已经默认不扫描,有兴趣的话可以查看另外一篇博客)
  2. 操作数据库这块,底层有一个类JdbcTemplateConfiguration,它帮我们自动注入了JdbcTemplate进容器中,我们可以直接操作
@ConditionalOnMissingBean({JdbcOperations.class})
class JdbcTemplateConfiguration {
    JdbcTemplateConfiguration() {
    }

    @Bean
    @Primary
    JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        Template template = properties.getTemplate();
        jdbcTemplate.setFetchSize(template.getFetchSize());
        jdbcTemplate.setMaxRows(template.getMaxRows());
        if (template.getQueryTimeout() != null) {
            jdbcTemplate.setQueryTimeout((int)template.getQueryTimeout().getSeconds());
        }

        return jdbcTemplate;
    }
}

到这里咱们就可以在测试类直接注入jdbcTemplate来进行操作了,这里就略过了