Spring Data之DataSource创建及源码分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

背景

俗话说万变不离其宗,代码中对数据库的操作,首先是要获取数据库连接,而Java中最原生的连接方式就是通过DriverManager

    private static String driver = "org.h2.Driver";
    private static String url = "jdbc:h2:mem:test";
    private static String user = "sa";

    @Test
    public void test_conn() throws SQLException {
        Connection conn = DriverManager.getConnection(url,user,"");
        Assert.assertNotNull(conn);
    }

在实际项目中通过DriverManager获取连接显然是不太合适的,因为每次getConnection都将与数据库进行一次交互,而数据库对连接的创建,用户名密码的校验也将消耗一定的资源。

DataSource的作用简单讲,即维护了一组可用的Connection,在获取连接时可直接从其维护的连接池中获取一个可用连接。数据源与连接池的关系是,连接池是数据源的实现手段。Spring Boot的默认数据源是Hikari DataSource,手动创建一个DataSource代码如下

    private static String url = "jdbc:h2:mem:test";
    private static String user = "sa";

    @Test
    public void test_hikari() throws SQLException {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl(url);
        config.setUsername(user);

        HikariDataSource dataSource = new HikariDataSource(config);
        Connection conn = dataSource.getConnection();
        Assert.assertNotNull(conn);
    }

在上篇博客中我们并没有编写Hikari的任何代码,但Hikari的数据源就自动创建了,这是为什么呢,接下来我们来分析一下。

数据源创建

Spring Boot是通过自动配置的方式来创建相关组件的,DataSource的自动配置入口类是DataSourceAutoConfiguration

    @Configuration
    // 当存在DataSource和EmbeddedDatabaseType类时生效
    @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
    @EnableConfigurationProperties(DataSourceProperties.class)
    @Import({ DataSourcePoolMetadataProvidersConfiguration.class,
            DataSourceInitializationConfiguration.class })
    public class DataSourceAutoConfiguration {

        /** * 数据源自动配置 */
        @Configuration
        @Conditional(PooledDataSourceCondition.class)
        @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
        @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
                DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
                DataSourceJmxConfiguration.class })
        protected static class PooledDataSourceConfiguration {

        }

        /** * 检查是否配置了spring.datasource.type属性 * 或者PooledDataSourceAvailableCondition条件为true */
        static class PooledDataSourceCondition extends AnyNestedCondition {

            PooledDataSourceCondition() {
                super(ConfigurationPhase.PARSE_CONFIGURATION);
            }

            @ConditionalOnProperty(prefix = "spring.datasource", name = "type")
            static class ExplicitType {

            }

            @Conditional(PooledDataSourceAvailableCondition.class)
            static class PooledDataSourceAvailable {

            }

        }

        /** * 测试支持的连接池是否可用 */
        static class PooledDataSourceAvailableCondition extends SpringBootCondition {

            @Override
            public ConditionOutcome getMatchOutcome(ConditionContext context,
                    AnnotatedTypeMetadata metadata) {
                //构建一个条件信息
                ConditionMessage.Builder message = ConditionMessage
                        .forCondition("PooledDataSource");
                //
                if (getDataSourceClassLoader(context) != null) {
                    return ConditionOutcome
                            .match(message.foundExactly("supported DataSource"));
                }
                return ConditionOutcome
                        .noMatch(message.didNotFind("supported DataSource").atAll());
            }

            /** * 获取DataSource的ClassLoader * 目的是检查默认支持的DataSource实现类是否存在 */
            private ClassLoader getDataSourceClassLoader(ConditionContext context) {
                Class<?> dataSourceClass = DataSourceBuilder
                        .findType(context.getClassLoader());
                return (dataSourceClass != null) ? dataSourceClass.getClassLoader() : null;
            }

        }

    }
    其中PooledDataSourceCondition继承了AnyNestedCondition,AnyNestedCondition的作用是当定义的条件中,只要有一个条件满足则整体返回匹配结果true。

总结一下,由于我们并没有配置spring.datasource.type,所以会继续根据PooledDataSourceAvailableCondition的结果判断,PooledDataSourceAvailableCondition判断的依据为是否能够加载到默认的DataSource实现类,通过DataSourceBuilder.findType()

    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" };

    public static Class<? extends DataSource> findType(ClassLoader classLoader) {
        for (String name : DATA_SOURCE_TYPE_NAMES) {
            try {
                return (Class<? extends DataSource>) ClassUtils.forName(name,
                        classLoader);
            }
            catch (Exception ex) {
                // Swallow and continue
            }
        }
        return null;
    }
    看到findType的实现就比较清楚了,由于依赖了hikari,那么com.zaxxer.hikari.HikariDataSource是能被正常加载到的。

到这里PooledDataSourceCondition也就匹配通过了,通过后进一步@Import里的操作,可以看到Import了DataSourceConfiguration.Hikari.class

    @ConditionalOnClass(HikariDataSource.class)
    @ConditionalOnMissingBean(DataSource.class)
    @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)
    static class Hikari {

        @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;
        }

    }

    @SuppressWarnings("unchecked")
    protected static <T> T createDataSource(DataSourceProperties properties,
            Class<? extends DataSource> type) {
        return (T) properties.initializeDataSourceBuilder().type(type).build();
    }
    这里通过@ConfigurationProperties(prefix = "spring.datasource.hikari")会读取application.properties中spring.datasource.hikari开头的相关配置到DataSourceProperties
    中,供createDataSource使用,如果没有设置,hikari则会采用默认值。

由于数据源的实现有多种

  1. com.zaxxer.hikari.HikariDataSource
  2. org.apache.tomcat.jdbc.pool.DataSource
  3. org.apache.commons.dbcp2.BasicDataSource

但他们都是继承自javax.sql.DataSource有通用的接口,所以在DataSourceBuilder的build中可以使用工具类直接创建

    public T build() {
        Class<? extends DataSource> type = getType();
        DataSource result = BeanUtils.instantiateClass(type);
        maybeGetDriverClassName();
        bind(result);
        return (T) result;
    }

根据方法的调用路径,这里返回的result会返回到@Bean注解的dataSource方法,那么Spring Context中就有了HikariDataSource实例,在后续的很多方法中需要注入DataSource时,也就有了来源。

最后贴一张整个逻辑的时序图,以便了解DataSource创建的整体流程
20191102100723\_1.png

其中PooledDataSourceCondition、PooledDataSourceAvailableCondition是DataSourceAutoConfiguration的静态内部类;Hikari是DataSourceConfiguration的静态内部类。


来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Data之DataSource创建及源码分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏