springboot2.0整合druid,以及springboot自动装配DataSource原理

1.springboot 目前的推荐版是2.1.2.RELEASE,咱们就以当前最新的推荐版为例java

2.建立项目,引入对应的jar包,我是以maven的形式mysql

父级的pomgit

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.2.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

 引入的包github

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

3.配置文件,更新详细的配置 请参考 阿里的github查看,网址;里面介绍了更详细的属性含义,以及多数据源的配置方式web

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 613814
    driver-class-name: com.mysql.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 8
      min-idle: 1
      max-active: 20
      max-wait: 60000
      time-between-eviction-runsMillis: 60000
      min-evictable-idle-timeMillis: 300000
      validation-query: select 'x' FROM DUAL
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-open-prepared-statements: 20
      max-pool-prepared-statement-per-connection-size: 20
      filters: stat
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      use-global-data-source-stat: true

4.测试类,debug运行,能够看到,DataSource接口的实现类就是durid的实现类,能够继续找到配置文件中的一些初始化参数spring

      initial-size: 8
      min-idle: 1
      max-active: 20
      max-wait: 60000sql

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { BadgerDruidApplication.class })
public class BadgerDruidApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    public void contextLoads() throws SQLException {
        Connection connection = dataSource.getConnection();
        PreparedStatement prepareStatement = connection
                .prepareStatement("select * from t_city where parent_id='-1'");
        ResultSet resultSet = prepareStatement.executeQuery();
        while (resultSet.next()) {
            String cityName = resultSet.getString("name");
            System.out.println(cityName);
        }
    }
}

至此,druid就与springboot2.0集成完成了;固然中间也会有springboot2.0 mysql的驱动包,跟服务端版本不匹配的异常apache

java.sql.SQLNonTransientConnectionException: CLIENT_PLUGIN_AUTH is requiredtomcat

替换下mysql驱动的版本springboot

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

5.下面讲一点springboot对DataSource的一些自动装配原理,以及另外一种druid的方式

spring-boot-autoconfigure-2.1.2.RELEASE.jar包下的org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration类

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
		DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {

	@Configuration
	@Conditional(EmbeddedDatabaseCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import(EmbeddedDataSourceConfiguration.class)
	protected static class EmbeddedDatabaseConfiguration {

	}

	@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 {

	}

	/**
	 * {@link AnyNestedCondition} that checks that either {@code spring.datasource.type}
	 * is set or {@link PooledDataSourceAvailableCondition} applies.
	 */
	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 {

		}

	}

咱们先看类上@Import({ DataSourcePoolMetadataProvidersConfiguration.class,DataSourceInitializationConfiguration.class })

DataSourcePoolMetadataProvidersConfiguration导入这个类的实例,点进去能够看到

@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)

@ConditionalOnClass(HikariDataSource.class)

@ConditionalOnClass(BasicDataSource.class)

这3个数据源的装配,都是导入了这些数据源,才会实例建立成功;而springboot2.0的默认生效的HikariDataSource数据源;如图所示,spring-boot-starter-jdbc默认依赖了;而在springboot1.x中,默认生效的org.apache.tomcat.jdbc.pool.DataSource.class

 

如今咱们看下根据spring.datasource.type指定类型的实例化,只拿了一部分主要的,咱们主要来看PooledDataSourceConfiguration 这个内部类

@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 {

	}

@Conditional(PooledDataSourceCondition.class) 根据判断条件,实例化这个类,指定了配置文件中,必须有type这个属性

实例化后,@Import 会导入DataSourceConfiguration 这个类;在DataSourceConfiguration这个类中,就是具体的DataSource接口实现;咱们默认导入web模块后,会导入tomcat,而且实例化org.apache.tomcat.jdbc.pool.DataSource

/**
	 * Tomcat Pool DataSource configuration.
	 */
	 //只要导入了这个org.apache.tomcat.jdbc.pool.DataSource.class,就会生效
	@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
	//没有DataSource的实例
	@ConditionalOnMissingBean(DataSource.class)
	//配置了spring.datasource.type 而且值为org.apache.tomcat.jdbc.pool.DataSource,才会生效;
	//matchIfMissing=true表示,就算没有配置spring.datasource.type,也会生效
	@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true)
	static class Tomcat {

		@Bean
		@ConfigurationProperties(prefix = "spring.datasource.tomcat")
		public org.apache.tomcat.jdbc.pool.DataSource dataSource(
				DataSourceProperties properties) {
			org.apache.tomcat.jdbc.pool.DataSource dataSource = 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;
		}

	}

另外springboot 默认支持 type 类型设置的数据源;

@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
            DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
            DataSourceJmxConfiguration.class })

具体的,接着DataSourceConfiguration类看就能够了,

下面说下,集成druid 在DataSourceConfiguration类,最后的部分

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

		@Bean
		public DataSource dataSource(DataSourceProperties properties) {
			return properties.initializeDataSourceBuilder().build();
		}

	}

若是上面,都不生效,就会执行这个;咱们直接看 return properties.initializeDataSourceBuilder().build(); 这个build方法;

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

拿到上述配置文件中的com.alibaba.druid.pool.DruidDataSource 这个类的全类名,反射实例化;至此,datasource的实例过程,就说完了;最后咱们看下,阿里官方封装的druid-spring-boot-starter,直接简单粗暴的建立了一个datasource的实现,有感性的朋友,也能够看下DruidDataSourceWrapper 的具体实现,就不在继续描述

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

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