1.springboot 目前的推荐版是2.1.2.RELEASE,我们就以当前最新的推荐版为例
2.创建项目,引入对应的jar包,我是以maven的形式
父级的pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
引入的包
<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查看,网址;里面介绍了更详细的属性含义,以及多数据源的配置方式
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
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的实现类,可以继续找到配置文件中的一些初始化参数
initial-size: 8
min-idle: 1
max-active: 20
max-wait: 60000
@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的驱动包,跟服务端版本不匹配的异常
java.sql.SQLNonTransientConnectionException: CLIENT_PLUGIN_AUTH is required
替换下mysql驱动的版本
<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();
}
}