Spring Boot 1.x版本默认使用的是tomcat.jdbc数据源,Sprng Boot 2.x版本默认使用的是Hikari数据源。如果使用其他数据源,还需要进行额外配置,比如阿里巴巴的Druid。
Druid是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0、DBCP 等连接池的优点,同时加入了日志监控。Druid 可以很好的监控数据库池连接和 SQL 的执行情况,天生就是针对监控而生的 数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。
Github地址:https://github.com/alibaba/druid/
Apache地址:http://druid.apache.org/
我们以MySQL数据库为例,介绍如何使用Druid,如何实现数据库监控。
1、创建项目
- 在Idea中通过Spring Initializr方式,创建名称为springboot0303-druid的SpringBoot项目并加入如图所示依赖。本案例中Spring Boot的版本为2.4.11。
- 项目创建成功后,其pom.xml配置如下。Druid依赖spring-jdbc,spring-boot-starter-jdbc依赖spring-jdbc,spring-boot-starter-data-jpa依赖spring-boot-starter-jdbc,因此,Druid的最低依赖是spring-jdbc,具体配置可根据实际需要调整。
4.0.0 org.springframework.boot spring-boot-starter-parent 2.4.11 com.company springboot0303-druid 0.0.1-SNAPSHOT springboot0303-druid Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true org.springframework.boot spring-boot-configuration-processor true org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-data-jdbc mysql mysql-connector-java runtime org.projectlombok lombok true org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok
3.在pom.xml中加入Druid的启动器依赖。阿里巴巴为了迎合Spring Boot项目而适配的Druid数据源启动器,当在pom.xml文件中引入该启动器后,不需要再进行其他额外配置,Spring Boot项目会自动识别配置Druid数据源。
com.alibaba druid-spring-boot-starter 1.2.8
4.运行主启动程序Application,控制台日志中有一条警告信息,而且导致应用程序启动失败。
... 2021-10-12 01:24:43.277 WARN 19552 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Failed to determine a suitable driver class 2021-10-12 01:24:43.282 INFO 19552 --- [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat] 2021-10-12 01:24:43.295 INFO 19552 --- [ restartedMain] ConditionevaluationReportLoggingListener : Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2021-10-12 01:24:43.313 ERROR 19552 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class Action: Consider the following: If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active). Process finished with exit code 0
通过分析上述日志得知,应用程序在启动时,配置名称叫dataSource的数据源Bean失败,是因为其url属性未指定导致不能确定合适的驱动程序类,而且也无法配置嵌入式数据源。
2、全局配置1.在全局配置文件application.properties中,增加spring.datasource.url配置。
spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&useSSL=false
2.重新运行主启动程序Application,应用程序启动失败。根据控制台日志可知,容器已经创建dataSource数据源Bean,其类名为com.alibaba.druid.pool.DruidDataSource,并一直使用当前Windows用户名和空密码尝试创建连接,但是失败了。
... 2021-10-12 10:54:05.258 ERROR 20820 --- [eate-1941501166] com.alibaba.druid.pool.DruidDataSource : create connection SQLException, url: jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&useSSL=false, errorCode 1045, state 28000 java.sql.SQLException: Access denied for user 'liuzh'@'localhost' (using password: NO) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.26.jar:8.0.26] at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.26.jar:8.0.26] at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:828) ~[mysql-connector-java-8.0.26.jar:8.0.26] at com.mysql.cj.jdbc.ConnectionImpl.(ConnectionImpl.java:448) ~[mysql-connector-java-8.0.26.jar:8.0.26] at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:241) ~[mysql-connector-java-8.0.26.jar:8.0.26] at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198) ~[mysql-connector-java-8.0.26.jar:8.0.26] at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1657) ~[druid-1.2.8.jar:1.2.8] at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1723) ~[druid-1.2.8.jar:1.2.8] at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2838) ~[druid-1.2.8.jar:1.2.8] Process finished with exit code -1
3.在全局配置文件application.properties中,增加spring.datasource.username和spring.datasource.password配置。
spring.datasource.username=root spring.datasource.password=123456
4.重新运行主启动程序Application,应用程序启动成功。
...
2021-10-12 11:01:32.207 INFO 11712 --- [ restartedMain] c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource
2021-10-12 11:01:32.309 INFO 11712 --- [ restartedMain] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
...
3、单元测试
1.在主启动程序单元测试类ApplicationTests中加入自动装配、显示dataSource信息的代码。
package com.company.project;
import com.alibaba.druid.pool.DruidDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.SQLException;
@SpringBootTest
public class ApplicationTests {
@Autowired
private DataSource dataSource;
@Test
void contextLoads() {
}
@Test
void showDataSource() {
DruidDataSource druidDataSource = (DruidDataSource) dataSource;
System.out.println("DataSource:" + druidDataSource);
System.out.println("DataSourceClassName:" + druidDataSource.getClass().getName());
try {
System.out.println("Connection:" + druidDataSource.getConnection().toString());
System.out.println("ConnectionIsClosed:" + druidDataSource.getConnection().isClosed());
System.out.println("ConnectionIsReadOnly:" + druidDataSource.getConnection().isReadOnly());
System.out.println("ConnectionAutoCommit:" + druidDataSource.getConnection().getAutoCommit());
System.out.println("ConnectionTransactionIsolation:" + druidDataSource.getConnection().getTransactionIsolation());
System.out.println("ConnectionNetworkTimeout:" + druidDataSource.getConnection().getNetworkTimeout());
System.out.println("ConnectionClassName:" + druidDataSource.getConnection().getClass().getName());
System.out.println("ConnectionCatalog:" + druidDataSource.getConnection().getCatalog());
System.out.println("InitialSize:" + druidDataSource.getInitialSize());
System.out.println("MinIdle:" + druidDataSource.getMinIdle());
System.out.println("MaxIdle:" + druidDataSource.getMaxIdle());
System.out.println("DataSource:" + dataSource);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
2.运行showDataSource()单元测试方法,执行成功。输出结果显示,初始化连接数量为1。
DataSource:{
CreateTime:"2021-10-12 11:57:20",
ActiveCount:0,
PoolingCount:1,
CreateCount:1,
DestroyCount:0,
CloseCount:1,
ConnectCount:1,
Connections:[
{ID:1279556617, ConnectTime:"2021-10-12 11:57:22", UseCount:1, LastActiveTime:"2021-10-12 11:57:22"}
]
}
DataSourceClassName:com.alibaba.druid.pool.DruidDataSource
Connection:com.mysql.cj.jdbc.ConnectionImpl@4c447c09
ConnectionIsClosed:false
ConnectionIsReadOnly:false
ConnectionAutoCommit:true
ConnectionTransactionIsolation:4
ConnectionNetworkTimeout:0
ConnectionClassName:com.alibaba.druid.pool.DruidPooledConnection
ConnectionCatalog:springbootdata
InitialSize:0
MinIdle:0
MaxIdle:8
DataSource:{
CreateTime:"2021-10-12 11:57:20",
ActiveCount:8,
PoolingCount:0,
CreateCount:8,
DestroyCount:0,
CloseCount:1,
ConnectCount:9,
Connections:[
]
}
4、其它参数
Druid数据源启动器内部已经初始化了一些运行参数(例如initialSize. maxActive等),如果开发过程中需要修改其运行参数,则必须在全局配置文件中修改。
1.在全局配置文件application.properties中添加druid运行参数配置。
# 数据源Druid # 数据源类型 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource # 初始化连接数 spring.datasource.initialSize=20 # 最小空闲数 spring.datasource.minIdle=10 # 最大连接数 spring.datasource.maxActive=100
添加上述配置后,会发现配置的initialSize、minIdle和maxActive属性底色为黄色(IDEA开发工具中的显示色),是因为在Spring Boot提供的数据源自动配置类org.springframework.boot.autoconfigure.jdbc.DataSourceProperties中,没有与这些参数对应的默认属性,所以这些设置的属性值无法识别和生效。因此需要编写一个自定义配置类,将配置文件中的属性注入到Druid数据源属性中。
2.在com.company.project.config包下创建一个DataSourceConfig自定义配置类对Druid数据源属性值进行注入。
package com.company.project.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource getDruid() {
return new DruidDataSource();
}
}
3.重新运行ApplicationTests类中的showDataSource()单元测试方法,执行成功。查看日志可知,初始化连接数为20,最后一次执行后的初始化连接数为12。
DataSource:{
CreateTime:"2021-10-12 11:55:14",
ActiveCount:0,
PoolingCount:20,
CreateCount:20,
DestroyCount:0,
CloseCount:1,
ConnectCount:1,
Connections:[
{ID:1789376127, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1808547623, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:2041504298, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:915540950, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1632716597, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1810790719, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1700751834, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1135936028, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:674667952, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:814300680, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:780566650, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1162400340, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1494158416, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:546242567, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:982565180, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:969502280, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:411408557, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1087519874, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:2096598149, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:247334525, ConnectTime:"2021-10-12 11:55:16", UseCount:1, LastActiveTime:"2021-10-12 11:55:16"}
]
}
DataSourceClassName:com.alibaba.druid.pool.DruidDataSource
Connection:com.mysql.cj.jdbc.ConnectionImpl@ebe067d
ConnectionIsClosed:false
ConnectionIsReadOnly:false
ConnectionAutoCommit:true
ConnectionTransactionIsolation:4
ConnectionNetworkTimeout:0
ConnectionClassName:com.alibaba.druid.pool.DruidPooledConnection
ConnectionCatalog:springbootdata
InitialSize:20
MinIdle:10
MaxIdle:8
DataSource:{
CreateTime:"2021-10-12 11:55:14",
ActiveCount:8,
PoolingCount:12,
CreateCount:20,
DestroyCount:0,
CloseCount:1,
ConnectCount:9,
Connections:[
{ID:1789376127, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1808547623, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:2041504298, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:915540950, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1632716597, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1810790719, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1700751834, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1135936028, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:674667952, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:814300680, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:780566650, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"},
{ID:1162400340, ConnectTime:"2021-10-12 11:55:16", UseCount:0, LastActiveTime:"2021-10-12 11:55:16"}
]
}
5、数据源监控
Druid数据源具有监控的功能,并提供了一个 web 界面方便用户查看和操作。但是,需要设置Druid的后台管理页面,比如登录账号、密码等。
1.在DataSourceConfig类中,增加监控管理后台的Servlet和web监控的filter。
package com.company.project.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource getDruid() {
return new DruidDataSource();
}
//配置 Druid 监控管理后台的Servlet;
//内置 Servlet 容器时没有web.xml文件,所以使用 Spring Boot 的注册 Servlet 方式
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
// 这些参数可以在 com.alibaba.druid.support.http.StatViewServlet 的父类 com.alibaba.druid.support.http.ResourceServlet 中找到
Map initParams = new HashMap<>();
initParams.put("loginUsername","admin");
initParams.put("loginPassword","123456");
initParams.put("allow",""); //默认就是允许所有访问
//deny:Druid 后台拒绝谁访问,表示禁止此ip访问
// initParams.put("deny","192.168.10.132");
bean.setInitParameters(initParams);
return bean;
}
//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
Map initParams = new HashMap<>();
initParams.put("exclusions","*.js,*.css,/druid/*");
bean.setInitParameters(initParams);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
2.重新运行主启动类Application成功后,使用浏览器访问http://localhost:8080/druid地址,在登录界面输入用户名和密码并点击登录按钮。
3.紧接着,就可以看到Druid监控的首页了。



