Springboot基础知识(15)- 整合 Druid 数据源
Spring Boot 2.x 默认使用 HikariCP 作为数据源,我们只要在项目中导入了 Spring Boot 的 JDBC 场景启动器,便可以使用 HikariCP 数据源获取数据库连接,对数据库进行增删改查等操作。
HikariCP 是目前市面上性能最好的数据源产品,但在实际的开发过程中,企业往往更青睐于另一款数据源产品:Druid,它是目前国内使用范围最广的数据源产品。
Druid 是阿里巴巴推出的一款开源的高性能数据源产品,Druid 支持所有 JDBC 兼容的数据库,包括 Oracle、MySQL、SQL Server 和 H2 等等。Druid 不仅结合了 C3P0、DBCP 和 PROXOOL 等数据源产品的优点,同时还加入了强大的监控功能。通过 Druid 的监控功能,可以实时观察数据库连接池和 SQL 的运行情况,帮助用户及时排查出系统中存在的问题。
Druid 不是 Spring Boot 内部提供的技术,它属于第三方技术,我们可以通过以下两种方式进行整合:
(1) 自定义整合 Druid
(2) 通过 starter 整合 Druid
本文将在 “ Springboot基础知识(08)- spring-boot-starter-web(Web启动器)” 里 SpringbootWeb 项目基础上,对这两种整合方式进行讲解。
1. 自定义整合 Druid
自定义整合 Druid 是指:根据 Druid 官方文档和自身的需求,通过手动创建 Druid 数据源的方式,将 Druid 整合到 Spring Boot 中。
1) 导入 JDBC、MariaDB 和 Druid 相关依赖包
访问 http://www.mvnrepository.com/,查询 spring-boot-starter-data-jdbc、druid 等
修改 pom.xml:
1 <project ... > 2 ... 3 4 <dependencies> 5 ... 6 7 <!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client --> 8 <dependency> 9 <groupId>org.mariadb.jdbc</groupId> 10 <artifactId>mariadb-java-client</artifactId> 11 </dependency> 12 13 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jdbc --> 14 <dependency> 15 <groupId>org.springframework.boot</groupId> 16 <artifactId>spring-boot-starter-data-jdbc</artifactId> 17 </dependency> 18 19 <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> 20 <dependency> 21 <groupId>com.alibaba</groupId> 22 <artifactId>druid</artifactId> 23 <version>1.2.8</version> 24 </dependency> 25 26 ... 27 </dependencies> 28 29 ... 30 </project>
注:Spring Boot 默认为数据库驱动程序做了版本仲裁,所以我们在导入数据库驱动时,可以不再声明版本。需要注意的是,数据库驱动的版本必须与数据库的版本相对应。
在IDE中项目列表 -> SpringbootWeb -> 点击鼠标右键 -> Maven -> Reload Project
2) 创建数据源
Spring Boot 使用 HikariCP 作为其默认数据源,但其中有一个十分重要的条件:当容器中没有 DataSource(数据源类)时,Spring Boot 才会使用 HikariCP 作为其默认数据源。
若我们向容器中添加 Druid 数据源类(DruidDataSource,继承自 DataSource)的对象时,Spring Boot 就会使用 Druid 作为其数据源,而不再使用 HikariCP。
创建 src/main/java/com/example/config/DruidDataSourceConfig.java 文件
1 package com.example.config; 2 3 import javax.sql.DataSource; 4 import java.sql.SQLException; 5 6 import com.alibaba.druid.pool.DruidDataSource; 7 import org.springframework.boot.context.properties.ConfigurationProperties; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Configuration; 10 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 11 12 @Configuration 13 public class DruidDataSourceConfig implements WebMvcConfigurer { 14 15 @ConfigurationProperties("spring.datasource") 16 @Bean 17 public DataSource dataSource() throws SQLException { 18 DruidDataSource druidDataSource = new DruidDataSource(); 19 20 /* 21 // 一般不建议将数据源属性硬编码到代码中,而应该在配置文件中进行配置(@ConfigurationProperties 绑定) 22 druidDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/bianchengbang_jdbc"); 23 druidDataSource.setUsername("root"); 24 druidDataSource.setPassword("root"); 25 druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); 26 */ 27 28 return druidDataSource; 29 } 30 }
修改 src/main/resources/application.properties 文件,添加如下代码:
1 # 数据源连接信息 2 spring.datasource.driver-class-name=org.mariadb.jdbc.Driver 3 spring.datasource.url=jdbc:mysql://localhost:3306/testdb 4 spring.datasource.username=root 5 spring.datasource.password=123456
注:配置类创建 Druid 数据源对象时,应该尽量避免将数据源信息(例如 url、username 和 password 等)硬编码到代码中,而应该通过 @ConfigurationProperties("spring.datasource") 注解,将 Druid 数据源对象的属性与配置文件中的以 “spring.datasource” 开头的配置进行绑定。
数据库配置参考 “ Springboot基础知识(14)- JDBC访问数据库、数据源配置原理 ” 的JDBC访问数据库部分。
3) 测试数据源
创建 src/test/com/example/SpringBootJDBCTest.java 文件
1 package com.example; 2 3 import java.sql.SQLException; 4 import javax.sql.DataSource; 5 6 import org.junit.jupiter.api.Test; 7 import org.springframework.beans.factory.annotation.Autowired; 8 import org.springframework.boot.test.context.SpringBootTest; 9 10 @SpringBootTest 11 public class SpringBootJDBCTest { 12 @Autowired 13 DataSource dataSource; // 数据源组件 14 15 @Test 16 void contextLoads() throws SQLException { 17 System.out.println("DataSource:" + dataSource.getClass()); 18 System.out.println("DataSource Connection:" + dataSource.getConnection()); 19 } 20 }
运行该测试代码,结果如下:
DataSource:class com.alibaba.druid.pool.DruidDataSource
DataSource Connection:org.mariadb.jdbc.MariaDbConnection@37e69c43
4) 开启 Druid 内置监控页面
Druid 内置提供了一个名为 StatViewServlet 的 Servlet,这个 Servlet 可以开启 Druid 的内置监控页面功能, 展示 Druid 的统计信息,它的主要用途如下:
(1) 提供监控信息展示的 html 页面
(2) 提供监控信息的 JSON API
注意:使用 StatViewServlet,建议使用 Druid 0.2.6 以上版本。
根据 Druid 官方文档-配置_StatViewServlet 配置,StatViewServlet 是一个标准的 javax.servlet.http.HttpServlet,想要开启 Druid 的内置监控页面,需要将该 Servlet 配置在 Web 应用中的 WEB-INF/web.xml 中,web.xml 中配置如下。
1 <servlet> 2 <servlet-name>DruidStatView</servlet-name> 3 <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class> 4 </servlet> 5 <servlet-mapping> 6 <servlet-name>DruidStatView</servlet-name> 7 <url-pattern>/druid/*</url-pattern> 8 </servlet-mapping>
Spring Boot 项目中是没有 WEB-INF/web.xml 的,因此我们可以在配置类中,通过 ServletRegistrationBean 将 StatViewServlet 注册到容器中,来开启 Druid 的内置监控页面。
在 DruidDataSourceConfig 配置类中添加以下代码,将 StatViewServlet 注入到容器中。
1 package com.example.config; 2 3 import javax.sql.DataSource; 4 import java.sql.SQLException; 5 6 import com.alibaba.druid.support.http.StatViewServlet; 7 import com.alibaba.druid.pool.DruidDataSource; 8 import org.springframework.boot.context.properties.ConfigurationProperties; 9 import org.springframework.context.annotation.Bean; 10 import org.springframework.context.annotation.Configuration; 11 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 12 import org.springframework.boot.web.servlet.ServletRegistrationBean; 13 14 @Configuration 15 public class DruidDataSourceConfig implements WebMvcConfigurer { 16 17 ... 18 19 @Bean 20 public ServletRegistrationBean statViewServlet() { 21 22 StatViewServlet statViewServlet = new StatViewServlet(); 23 24 // 向容器中注入 StatViewServlet,并将其路径映射设置为 /druid/* 25 ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(statViewServlet, "/druid/*"); 26 27 // 配置监控页面访问的账号和密码(选配) 28 servletRegistrationBean.addInitParameter("loginUsername", "admin"); 29 servletRegistrationBean.addInitParameter("loginPassword", "123456"); 30 31 return servletRegistrationBean; 32 } 33 }
访问 Druid 的内置监控页面:http://localhost:9090/druid/login.html
Username: admin
Password: 123456
5) 开启 SQL 监控
Druid 内置提供了一个 StatFilter,通过它可以开启 Druid 的 SQL 监控功能,对 SQL 进行监控。
StatFilter 的别名是 stat,这个别名的映射配置信息保存在 druid-xxx.jar!/META-INF/druid-filter.properties 中。Druid 官方文档-配置_StatFilter 中给出了在 Spring 中配置该别名(stat)开启 Druid SQL 监控的方式,配置如下。
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
...
<property name="filters" value="stat" />
</bean>
根据以上配置我们可以看出,只要在 dataSource 的 Bean 中添加一个取值为“stat”的“filters”属性,就能开启 Druid SQL 监控,因此我们只要将该配置转换为在配置类中进行即可,代码如下。
修改 src/main/java/com/example/config/DruidDataSourceConfig.java 文件
1 package com.example.config; 2 3 import javax.sql.DataSource; 4 import java.sql.SQLException; 5 6 import com.alibaba.druid.pool.DruidDataSource; 7 import org.springframework.boot.context.properties.ConfigurationProperties; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Configuration; 10 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 11 12 @Configuration 13 public class DruidDataSourceConfig implements WebMvcConfigurer { 14 15 ... 16 17 @ConfigurationProperties("spring.datasource") 18 @Bean 19 public DataSource dataSource() throws SQLException { 20 DruidDataSource druidDataSource = new DruidDataSource(); 21 22 // 设置 filters 属性值为 stat,开启 SQL 监控 23 druidDataSource.setFilters("stat"); 24 25 return druidDataSource; 26 } 27 }
为了验证 Druid SQL 监控是否开启,我们需要在 IndexController 中添加一个能够查询数据库的方法,代码如下。
1 @Controller 2 public class IndexController { 3 // 自动装配 jdbcTemplate 4 @Autowired 5 JdbcTemplate jdbcTemplate; 6 7 @ResponseBody 8 @GetMapping("/stat") 9 public String stat() { 10 String SQL = "SELECT count(*) from `user`"; 11 Integer integer = jdbcTemplate.queryForObject(SQL, Integer.class); 12 return integer.toString(); 13 } 14 }
访问:http://localhost:9090/stat
再访问 Druid 的内置监控页面,切换到 SQL 监控模块,可以看到 Druid SQL 监控已经开启。
6) 开启防火墙
Druid 内置提供了一个 WallFilter,使用它可以开启防火墙功能,防御 SQL 注入攻击。
WallFilter 的别名是 wall,这个别名映射配置信息保存在 druid-xxx.jar!/META-INF/druid-filter.properties 中。Druid 官方文档-配置 wallfilter 中给出了在 Spring 中使用该别名(wall)开启防火墙的方式,配置如下。
1 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 2 ... 3 <property name="filters" value="wall" /> 4 </bean>
WallFilter 可以结合其他 Filter 一起使用,例如:
1 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 2 ... 3 <property name="filters" value="wall,stat"/> 4 </bean>
根据以上配置我们可以看出,只要在 dataSource 的 Bean 中添加一个取值为“wall”的“filters”属性,就能开启 Druid 的防火墙功能,因此我们只需要在配置类中为 dataSource 的 filters 属性再添加一个“wall”即可(多个属性值之间使用逗号“,”隔开),代码如下。
1 @ConfigurationProperties("spring.datasource") 2 @Bean 3 public DataSource dataSource() throws SQLException { 4 DruidDataSource druidDataSource = new DruidDataSource(); 5 //同时开启 sql 监控(stat) 和防火墙(wall),中间用逗号隔开。 6 //开启防火墙能够防御 SQL 注入攻击 7 druidDataSource.setFilters("stat,wall"); 8 return druidDataSource; 9 }
访问:http://localhost:9090/stat
再访问 Druid 的内置监控页面,切换到 SQL 防火墙,可以看到 Druid 防火墙已经开启。
7) 开启 Web-JDBC 关联监控
Druid 还内置提供了一个名为 WebStatFilter 的过滤器,它可以用来监控与采集 web-jdbc 关联监控的数据。
根据 Druid 官方文档-配置_配置WebStatFilter,想要开启 Druid 的 Web-JDBC 关联监控,只需要将 WebStatFilter 配置在 Web 应用中的 WEB-INF/web.xml 中即可,web.xml 配置如下。
1 <filter> 2 <filter-name>DruidWebStatFilter</filter-name> 3 <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class> 4 <init-param> 5 <param-name>exclusions</param-name> 6 <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value> 7 </init-param> 8 </filter> 9 <filter-mapping> 10 <filter-name>DruidWebStatFilter</filter-name> 11 <url-pattern>/*</url-pattern> 12 </filter-mapping>
Spring Boot 项目中是没有 WEB-INF/web.xml 的,但是我们可以在配置类中,通过 FilterRegistrationBean 将 WebStatFilter 注入到容器中,来开启 Druid 的 Web-JDBC 关联监控。
修改 src/main/java/com/example/config/DruidDataSourceConfig.java 文件
1 package com.example.config; 2 3 import javax.sql.DataSource; 4 import java.sql.SQLException; 5 import java.util.Arrays; 6 7 import org.springframework.boot.context.properties.ConfigurationProperties; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Configuration; 10 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 11 import org.springframework.boot.web.servlet.FilterRegistrationBean; 12 13 @Configuration 14 public class DruidDataSourceConfig implements WebMvcConfigurer { 15 16 ... 17 @Bean 18 public FilterRegistrationBean druidWebStatFilter() { 19 WebStatFilter webStatFilter = new WebStatFilter(); 20 FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(webStatFilter); 21 22 // 监控所有的访问 23 filterRegistrationBean.setUrlPatterns(Arrays.asList("/*")); 24 // 监控访问不包括以下路径 25 filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); 26 27 return filterRegistrationBean; 28 } 29 }
访问:http://localhost:9090/stat 和 http://localhost:9090/hello
再访问 Druid 的内置监控页面,切换到 Web 应用模块,可以看到 Druid 的 Web 监控已经开启,与此同时,URI 监控和 Session 监控也都被开启。
2. 通过 starter 整合 Druid
Druid 可以说是国内使用最广泛的数据源连接池产品,但到目前为止 Spring Boot 官方只对 Hikari、Tomcat、Dbcp2 和 OracleUcp 等 4 种数据源产品提供了自动配置支持,对于其他的数据源连接池产品(包括 Druid),则并没有提供自动配置支持。这就导致用户只能通过自定义的方式(第一种整合方式)整合 Druid,非常繁琐。
为了解决这一问题,于是阿里官方提供了 Druid Spring Boot Starter,它可以帮助我们在 Spring Boot 项目中,轻松地整合 Druid 的数据库连接池和监控功能。
1) 导入 druid-spring-boot-starter 相关依赖包
访问 http://www.mvnrepository.com/,查询 druid-spring-boot-starter 等
修改 pom.xml:
1 <project ... > 2 ... 3 4 <dependencies> 5 ... 6 7 <!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client --> 8 <dependency> 9 <groupId>org.mariadb.jdbc</groupId> 10 <artifactId>mariadb-java-client</artifactId> 11 </dependency> 12 13 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jdbc --> 14 <dependency> 15 <groupId>org.springframework.boot</groupId> 16 <artifactId>spring-boot-starter-data-jdbc</artifactId> 17 </dependency> 18 19 <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> 20 <dependency> 21 <groupId>com.alibaba</groupId> 22 <artifactId>druid-spring-boot-starter</artifactId> 23 <version>1.2.8</version> 24 </dependency> 25 26 ... 27 </dependencies> 28 29 ... 30 </project>
注:Spring Boot 默认为数据库驱动程序做了版本仲裁,所以我们在导入数据库驱动时,可以不再声明版本。需要注意的是,数据库驱动的版本必须与数据库的版本相对应。
在IDE中项目列表 -> SpringbootWeb -> 点击鼠标右键 -> Maven -> Reload Project
2) 配置属性
Druid Spring Boot Starter 已经将 Druid 数据源中的所有模块都进行默认配置,我们也可以通过 Spring Boot 配置文件(application.properties/application.yml)来修改 Druid 各个模块的配置,否则将使用默认配置。
在 Spring Boot 配置文件中配置以下内容:
(1) JDBC 通用配置
(2) Druid 数据源连接池配置
(3) Druid 监控配置
(4) Druid 内置 Filter 配置
本文以 application.properties 为例。
(1) JDBC 通用配置
1 spring.datasource.username=root # 数据库登陆用户名 2 spring.datasource.password=123456 # 数据库登陆密码 3 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/testdb # 数据库url 4 spring.datasource.driver-class-name=org.mariadb.jdbc.Driver # 数据库驱动
(2) Druid 数据源连接池配置
可以在 Spring Boot 的配置文件中对 Druid 数据源连接池进行配置,示例代码如下。
1 spring.datasource.druid.initial-size=5 # 初始化连接大小 2 spring.datasource.druid.min-idle=5 # 最小连接池数量 3 spring.datasource.druid.max-active=20 # 最大连接池数量 4 spring.datasource.druid.max-wait=60000 # 获取连接时最大等待时间,单位毫秒 5 spring.datasource.druid.time-between-eviction-runs-millis=60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 6 spring.datasource.druid.min-evictable-idle-time-millis=300000 # 配置一个连接在池中最小生存的时间,单位是毫秒 7 spring.datasource.druid.validation-query=SELECT 1 FROM DUAL # 测试连接 8 spring.datasource.druid.test-while-idle=true # 申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性 9 spring.datasource.druid.test-on-borrow=false # 获取连接时执行检测,建议关闭,影响性能 10 spring.datasource.druid.test-on-return=false # 归还连接时执行检测,建议关闭,影响性能 11 spring.datasource.druid.pool-prepared-statements=false # 是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭 12 spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 # 开启poolPreparedStatements 后生效 13 spring.datasource.druid.filters=stat,wall # 配置扩展插件,常用的插件有 -> stat:监控统计 wall:防御sql注入 14 spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
(3) Druid 监控配置
可以在 Spring Boot 的配置文件中对 Druid 内置监控页面、Web-JDBC 关联监控和 Spring 监控等功能进行配置,示例代码如下。
1 # StatViewServlet 配置 2 spring.datasource.druid.stat-view-servlet.enabled=true # 是否开启内置监控页面,默认值为 false 3 spring.datasource.druid.stat-view-servlet.url-pattern=/druid/* # StatViewServlet 的映射路径,即内置监控页面的访问地址 4 spring.datasource.druid.stat-view-servlet.reset-enable=true # 是否启用重置按钮 5 spring.datasource.druid.stat-view-servlet.login-username=admin # 内置监控页面的登录页用户名 username 6 spring.datasource.druid.stat-view-servlet.login-password=123456 # 内置监控页面的登录页密码 password 7 8 # WebStatFilter 配置 9 spring.datasource.druid.web-stat-filter.enabled=true # 是否开启内置监控中的 Web-jdbc 关联监控的数据 10 spring.datasource.druid.web-stat-filter.url-pattern=/* # 匹配路径 11 spring.datasource.druid.web-stat-filter.session-stat-enable=true # 是否监控session 12 spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/* # 排除路径 13 14 # Spring 监控配置 15 spring.datasource.druid.web-stat-filter.aop-patterns=com.example.* # Spring监控AOP切入点,如x.y.z.abc.*, 配置多个英文逗号分隔
测试,访问:http://localhost:9090/stat
再访问 Druid 的内置监控页面 http://localhost:9090/druid/login.html ,查看各相关监控信息。
(4) Druid 内置 Filter 配置
Druid Spring Boot Starter 对以下 Druid 内置 Filter,都提供了默认配置:
StatFilter
WallFilter
ConfigFilter
EncodingConvertFilter
Slf4jLogFilter
Log4jFilter
Log4j2Filter
CommonsLogFilter
可以通过 spring.datasource.druid.filters=stat,wall ... 的方式来启用相应的内置 Filter,不过这些 Filter 使用的都是默认配置。如果默认配置不能满足我们的需求,我们还可以在配置文件使用 spring.datasource.druid.filter.* 对这些 Filter 进行配置,示例代码如下。
1 # 对配置已开启的 filters 即 stat(sql 监控) wall(防火墙) 2 3 # 配置 StatFilter (SQL监控配置) 4 spring.datasource.druid.filter.stat.enabled=true # 开启 SQL 监控 5 spring.datasource.druid.filter.stat.slow-sql-millis=1000 # 慢查询 6 spring.datasource.druid.filter.stat.log-slow-sql=true # 记录慢查询 SQL 7 8 # 配置 WallFilter (防火墙配置) 9 spring.datasource.druid.filter.wall.enabled=true # 开启防火墙 10 spring.datasource.druid.filter.wall.config.update-allow=true # 允许更新操作 11 spring.datasource.druid.filter.wall.config.drop-table-allow=false # 禁止删表操作 12 spring.datasource.druid.filter.wall.config.insert-allow=true # 允许插入操作 13 spring.datasource.druid.filter.wall.config.delete-allow=true # 删除数据操作
注:在配置 Druid 内置 Filter 时,需要先将对应 Filter 的 enabled 设置为 true,否则内置 Filter 的配置不会生效。
以上所有内容都只是示例配置,Druid Spring Boot Starter 并不是只支持以上属性,它支持 DruidDataSource 内所有具有 setter 方法的属性。
通过 Druid Spring Boot Starter 整合 Druid 数据源,则更加方便快捷,大大简化了整合过程,但无法清晰地了解 Druid 的功能内部的实现方式和原理。