简介:Druid是阿里巴巴开发的一款高性能Java数据库连接池组件,具备连接管理、性能监控、SQL解析、安全过滤和健康检查等功能。本文围绕Druid连接池的配置与使用展开,涵盖Maven依赖引入、Spring Boot配置方式、数据库工具类封装、监控页面设置以及SQL安全防护等内容,帮助开发者掌握Druid在实际项目中的应用,提升数据库访问效率与系统稳定性。
1. Druid连接池简介与核心特性
1.1 什么是Druid连接池
Druid 是阿里巴巴开源的一款高性能、功能全面的数据库连接池组件,专为 Java 应用设计。它不仅实现了标准的 JDBC 接口,提供数据库连接的高效管理,还内置了强大的监控、统计、SQL解析和防御 SQL 注入等功能,广泛应用于企业级项目中。
相较于传统的连接池(如 C3P0、DBCP),Druid 在性能、可维护性和安全性方面均有显著提升。它支持多种数据库类型(如 MySQL、Oracle、PostgreSQL 等),并可与 Spring、Spring Boot 等主流框架无缝集成,是现代 Java Web 项目中推荐使用的连接池解决方案之一。
2. Maven项目中引入Druid依赖配置
Maven 作为 Java 项目中主流的构建与依赖管理工具,极大地简化了项目的构建流程和依赖版本管理。在实际开发中,引入 Druid 连接池通常基于 Maven 进行配置。本章将围绕 Maven 项目结构、依赖管理机制,详细讲解如何正确引入 Druid,并解决常见配置问题,最终验证连接池的集成效果。
2.1 Maven项目结构与依赖管理基础
在深入了解如何引入 Druid 之前,有必要掌握 Maven 项目的基本结构和依赖管理机制,这是正确配置 Druid 的前提。
2.1.1 Maven的核心配置文件pom.xml解析
Maven 项目的核心配置文件是 pom.xml (Project Object Model),它定义了项目的元数据、依赖项、插件配置等信息。一个典型的 pom.xml 文件结构如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>druid-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- 依赖项 -->
</dependencies>
<build>
<plugins>
<!-- 插件配置 -->
</plugins>
</build>
</project>
参数说明:
-
<groupId>:项目组织唯一标识符,通常使用反向域名格式(如com.example)。 -
<artifactId>:项目名称。 -
version:项目版本号。 -
dependencies:定义项目所依赖的外部库(如 Druid)。 -
build/plugins:配置构建过程中的插件(如编译插件、打包插件等)。
该文件是 Maven 项目的核心,任何依赖项的引入都需在 <dependencies> 标签中添加对应的 <dependency> 块。
2.1.2 依赖作用域与版本管理
Maven 的依赖作用域(Scope)决定了依赖项在不同构建阶段和类路径中的可用性。常见的作用域包括:
| 作用域 | 描述 |
|---|---|
| compile | 默认作用域,适用于所有阶段(编译、测试、运行)。 |
| provided | 由 JDK 或容器提供,如 servlet-api ,打包时不包含该依赖。 |
| runtime | 只在运行和测试阶段有效,编译时不参与。 |
| test | 仅在测试阶段使用,如 JUnit、Mockito 等。 |
| system | 本地依赖,需手动指定路径。 |
| import | 仅用于 <dependencyManagement> 中,导入其他 POM 的依赖配置。 |
版本管理策略
Maven 支持多种版本控制方式,推荐使用统一版本管理:
<properties>
<druid.version>1.2.18</druid.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
这样可以在 <dependencies> 中直接引用:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
版本统一管理有助于避免多个模块之间的版本冲突。
2.2 在Maven项目中引入Druid
Druid 的引入主要通过在 pom.xml 中添加其 Maven 依赖实现,同时可能需要根据项目框架(如 Spring)进行额外配置。
2.2.1 添加Druid的Maven依赖
在 pom.xml 的 <dependencies> 中添加如下依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.18</version>
</dependency>
如果你使用的是 Spring Boot,还可以引入专为 Spring 优化的 Druid starter:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.18</version>
</dependency>
依赖树结构(mermaid流程图)
graph TD
A[druid] --> B(druid-core)
A --> C(druid-web)
A --> D(druid-spring-boot-starter)
D --> E[spring-boot-autoconfigure]
D --> F[spring-boot-starter]
2.2.2 配置Druid与Spring框架的兼容性
对于 Spring 项目,尤其是 Spring Boot,Druid 提供了自动装配机制。你可以在 application.properties 或 application.yml 中配置数据源。
例如在 application.yml 中配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
该配置启用了 Druid 数据源,并设置了连接池参数。Spring Boot 会自动识别并装配 DruidDataSource 。
2.3 常见配置问题与解决方法
在引入 Druid 的过程中,可能会遇到版本冲突、类加载失败或数据库驱动不兼容等问题。
2.3.1 版本冲突与类加载问题
现象 :启动项目时报 NoClassDefFoundError 或 ClassNotFoundException ,如:
java.lang.NoClassDefFoundError: com/alibaba/druid/pool/DruidDataSource
原因 :Maven 依赖冲突,或依赖未正确加载。
解决方案 :
- 检查
pom.xml中是否正确添加了 Druid 依赖。 - 使用
mvn dependency:tree查看依赖树,排查冲突版本。 - 排除冲突依赖,例如:
<dependency>
<groupId>some-group</groupId>
<artifactId>some-artifact</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</exclusion>
</exclusions>
</dependency>
2.3.2 不同数据库驱动的适配配置
Druid 支持多种数据库,但需要配合相应的 JDBC 驱动。例如 MySQL、PostgreSQL、Oracle 等都需要单独引入驱动依赖。
以 MySQL 为例:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
在 application.yml 中配置:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
常见数据库驱动对照表:
| 数据库类型 | JDBC URL示例 | 驱动类名 |
|---|---|---|
| MySQL | jdbc:mysql://localhost:3306/dbname | com.mysql.cj.jdbc.Driver |
| PostgreSQL | jdbc:postgresql://localhost:5432/dbname | org.postgresql.Driver |
| Oracle | jdbc:oracle:thin:@localhost:1521:orcl | oracle.jdbc.OracleDriver |
| SQL Server | jdbc:sqlserver://localhost:1433;databaseName=dbname | com.microsoft.sqlserver.jdbc.SQLServerDriver |
2.4 验证Druid连接池的集成效果
完成依赖配置后,应通过编写测试代码和分析日志来验证连接池是否正常初始化。
2.4.1 编写测试代码验证连接池初始化
在 Spring Boot 项目中,可以通过注入 DataSource 来获取连接:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@Component
public class DataSourceTester {
@Autowired
private DataSource dataSource;
public void testConnection() {
try (Connection connection = dataSource.getConnection()) {
System.out.println("Connection obtained successfully!");
System.out.println("Connection URL: " + connection.getMetaData().getURL());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
逻辑分析:
- 使用
@Autowired注入 Spring 管理的DataSource实例。 - 调用
getConnection()获取数据库连接。 - 使用
try-with-resources自动关闭连接,防止资源泄漏。 - 输出连接信息以确认连接池初始化成功。
2.4.2 连接池初始化日志分析
在应用启动时,Druid 会在日志中输出连接池的初始化信息,例如:
INFO c.a.d.p.DruidDataSource - {dataSource-1} inited
INFO c.a.d.p.DruidDataSource - initialSize: 5
INFO c.a.d.p.DruidDataSource - minIdle: 5
INFO c.a.d.p.DruidDataSource - maxActive: 20
这些日志说明连接池已成功初始化,并应用了配置的参数。如出现错误,日志中也会输出详细的异常信息,帮助定位问题。
日志分析建议:
- 关注
DruidDataSource的初始化状态。 - 检查是否有数据库连接失败的异常(如用户名密码错误、驱动未加载等)。
- 观察连接池大小是否符合预期配置。
通过本章内容,读者可以掌握 Maven 项目中如何正确引入和配置 Druid 连接池,解决常见的依赖和数据库驱动问题,并通过测试代码和日志验证集成效果。下一章将深入讲解在 Spring Boot 中如何更灵活地配置 Druid 数据源。
3. Spring Boot中Druid连接池配置详解
在Spring Boot项目中,数据库连接池是系统性能和稳定性的重要保障。Druid作为一款功能强大、性能优异的连接池,广泛应用于企业级Java项目中。本章将围绕Spring Boot项目,详细讲解Druid连接池的集成与配置方法,帮助开发者在实际开发中灵活运用,并掌握生产环境下的最佳实践。
3.1 Spring Boot数据源配置机制
Spring Boot 提供了自动装配(Auto-Configuration)机制来简化数据源的配置。通过配置文件(如 application.yml 或 application.properties )即可快速完成数据库连接池的初始化。
3.1.1 application.yml与application.properties的区别
在Spring Boot中, application.properties 和 application.yml 都可以用于配置数据源。它们的区别主要体现在语法结构和可读性上:
| 特性 | application.properties | application.yml |
|---|---|---|
| 格式 | key=value | YAML结构化格式(缩进表示层级) |
| 可读性 | 一般 | 更清晰,层级结构一目了然 |
| 嵌套配置 | 不方便 | 支持多层嵌套配置 |
| 示例 | spring.datasource.url=jdbc:mysql://… | spring.datasource.url: jdbc:mysql://… |
例如,使用 application.properties 配置Druid数据源如下:
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
而使用 application.yml 则更为清晰:
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
3.1.2 自动装配原理与数据源注入
Spring Boot 的自动装配机制基于 spring-boot-autoconfigure 模块,它通过 DataSourceAutoConfiguration 类来加载数据源。Spring Boot 默认使用 HikariCP ,但如果在配置文件中指定了 spring.datasource.type ,则会加载对应的连接池实现。
在自动装配过程中,Spring Boot 会通过反射创建 DataSource 实例,并将其注入到 Spring 容器中,供后续的 JdbcTemplate 、 MyBatis 等组件使用。
3.2 Druid在Spring Boot中的集成方式
在Spring Boot中集成Druid主要有两种方式:使用starter方式集成和手动配置DruidDataSource。
3.2.1 使用starter方式集成Druid
目前社区中有一些封装好的Druid Starter,例如 druid-spring-boot-starter ,它简化了Druid的集成步骤。
步骤如下:
- 添加依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.18</version>
</dependency>
- 配置application.yml:
spring:
datasource:
druid:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
validation-query: SELECT 1
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
- 启用Druid监控页面(可选):
spring:
datasource:
druid:
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: admin
reset-enable: false
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.css,/druid/*"
这种方式适用于大多数Spring Boot项目,配置简洁,适合快速集成。
3.2.2 手动配置DruidDataSource
手动配置方式更灵活,适合需要对Druid进行深度定制的项目。
代码示例:
@Configuration
public class DruidConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid")
public DataSource druidDataSource() {
return DruidDataSourceBuilder.create().build();
}
// 配置监控页面
@Bean
public ServletRegistrationBean<StatViewServlet> statViewServlet() {
ServletRegistrationBean<StatViewServlet> bean =
new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin"); // 登录用户名
initParams.put("loginPassword", "admin"); // 登录密码
initParams.put("allow", ""); // 允许所有IP访问
bean.setInitParameters(initParams);
return bean;
}
// 配置过滤器
@Bean
public FilterRegistrationBean<WebStatFilter> webStatFilter() {
FilterRegistrationBean<WebStatFilter> bean =
new FilterRegistrationBean<>(new WebStatFilter());
bean.addUrlPatterns("/*");
bean.addInitParameter("exclusions", "*.js,*.css,/druid/*");
return bean;
}
}
参数说明:
- loginUsername 和 loginPassword :用于访问Druid监控页面的登录账号和密码。
- allow :允许访问监控页面的IP地址列表,空值表示不限制。
- exclusions :需要排除的静态资源路径。
3.3 关键配置参数详解
Druid提供了丰富的配置参数,开发者可以根据业务需求进行优化。以下是一些常用参数及其作用。
3.3.1 初始化连接数、最大连接数与超时设置
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
| 参数 | 含义 |
|---|---|
| initial-size | 初始化连接数 |
| min-idle | 最小空闲连接数 |
| max-active | 最大连接数 |
| max-wait | 获取连接的最大等待时间(毫秒) |
建议:
- 生产环境建议设置 initial-size 和 min-idle 相等,避免连接池初始化慢的问题。
- max-active 应根据数据库的最大连接数限制合理设置,避免资源耗尽。
- max-wait 不宜过长,建议控制在 10s 以内。
3.3.2 空闲连接回收策略与连接检测机制
test-while-idle: true
test-on-borrow: false
test-on-return: false
validation-query: SELECT 1
| 参数 | 含义 |
|---|---|
| test-while-idle | 空闲时是否验证连接有效性 |
| test-on-borrow | 获取连接时是否验证(不推荐) |
| test-on-return | 归还连接时是否验证(不推荐) |
| validation-query | 验证SQL语句 |
建议:
- test-while-idle 建议设置为 true ,并配合 time-between-eviction-runs-millis 设置回收周期。
- 避免使用 test-on-borrow 和 test-on-return ,因为会显著降低性能。
- validation-query 推荐使用轻量SQL,如 SELECT 1 。
3.4 配置优化建议与生产环境适配
3.4.1 多数据源配置实践
在微服务或模块化项目中,经常需要配置多个数据源。Spring Boot 支持通过 @Configuration + @Bean 的方式配置多个 DataSource 。
@Bean(name = "dataSource1")
@ConfigurationProperties(prefix = "spring.datasource.druid.datasource1")
public DataSource dataSource1() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "dataSource2")
@ConfigurationProperties(prefix = "spring.datasource.druid.datasource2")
public DataSource dataSource2() {
return DruidDataSourceBuilder.create().build();
}
配置文件示例:
spring:
datasource:
druid:
datasource1:
url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
datasource2:
url: jdbc:mysql://localhost:3306/db2
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
逻辑分析:
- 通过 @Bean(name = "xxx") 定义多个数据源 Bean。
- 在业务代码中通过 @Qualifier("xxx") 指定使用哪个数据源。
- 配置文件通过前缀区分不同数据源的配置。
3.4.2 性能调优与常见配置误区
性能调优建议:
- 合理设置 max-active ,避免数据库连接数被耗尽。
- 启用 pool-prepared-statements ,提升SQL执行效率。
- 设置 removeAbandoned 和 removeAbandonedTimeout ,防止连接泄漏。
常见配置误区:
- 过度开启 test-on-borrow ,导致性能下降。
- 忽略 time-between-eviction-runs-millis 配置,空闲连接无法及时回收。
- 没有设置监控页面密码,导致安全隐患。
总结
本章围绕Spring Boot项目,详细讲解了Druid连接池的配置方式,包括使用starter方式集成和手动配置DataSource。我们还深入分析了关键配置参数的作用,并结合生产环境给出了优化建议和多数据源配置方案。通过本章内容,读者应能够掌握Druid在Spring Boot中的完整配置流程,并具备在实际项目中灵活应用的能力。
下一章将继续深入,介绍如何基于Druid封装数据库的增删改查操作,提升数据库访问层的封装效率与可维护性。
4. 使用Druid封装数据库增删改查操作
4.1 JDBC编程基础与Druid集成
4.1.1 原生JDBC操作流程
Java Database Connectivity(JDBC)是 Java 提供的一套用于与数据库交互的标准接口。其核心流程包括以下几个步骤:
- 加载数据库驱动 :通过
Class.forName()方法加载数据库的 JDBC 驱动类。 - 建立数据库连接 :通过
DriverManager.getConnection()方法获取数据库连接。 - 创建 SQL 执行对象 :使用
Connection创建Statement或PreparedStatement。 - 执行 SQL 语句 :调用
executeQuery()或executeUpdate()执行查询或更新操作。 - 处理结果集 :对于查询操作,使用
ResultSet获取返回数据。 - 关闭资源 :依次关闭
ResultSet、Statement、Connection。
以下是一个典型的 JDBC 查询操作示例:
public List<String> queryData() {
List<String> result = new ArrayList<>();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
// 3. 创建 Statement
stmt = conn.createStatement();
// 4. 执行查询
rs = stmt.executeQuery("SELECT name FROM users");
// 5. 处理结果集
while (rs.next()) {
result.add(rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 6. 关闭资源
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return result;
}
代码逻辑分析 :
- 使用了try-catch-finally结构确保资源释放。
- 每次执行都需要手动加载驱动、获取连接、执行SQL、处理结果集、关闭资源,代码冗余且易出错。
4.1.2 使用Druid简化JDBC操作
Druid 提供了高效的连接池管理能力,同时封装了常见的 JDBC 操作流程,使得开发者可以更专注于业务逻辑的实现。它通过 DruidDataSource 管理连接池,并提供了 JdbcTemplate 风格的封装方式。
以下是使用 Druid 的查询操作简化实现:
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class UserDao {
private DruidDataSource dataSource;
public UserDao(DruidDataSource dataSource) {
this.dataSource = dataSource;
}
public List<String> queryUsers() {
List<String> users = new ArrayList<>();
String sql = "SELECT name FROM users";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
users.add(rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
}
return users;
}
}
代码逻辑分析 :
- 使用try-with-resources自动关闭资源,避免资源泄漏。
-dataSource.getConnection()从连接池中获取连接,性能更高。
- 使用PreparedStatement防止 SQL 注入。
- 封装成UserDao类,提高代码复用性与可维护性。
4.2 封装通用的DAO工具类
4.2.1 增删改操作的统一接口封装
为了进一步提高代码的通用性与可维护性,我们可以将增删改操作封装为统一接口,支持动态 SQL 和参数传递。
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JdbcUtils {
private DruidDataSource dataSource;
public JdbcUtils(DruidDataSource dataSource) {
this.dataSource = dataSource;
}
public int update(String sql, Object... params) {
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
return ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("执行SQL更新失败", e);
}
}
}
参数说明 :
-sql:预编译 SQL 语句,使用?占位符。
-params:参数数组,用于替换 SQL 中的?。使用示例 :
JdbcUtils jdbcUtils = new JdbcUtils(dataSource);
jdbcUtils.update("INSERT INTO users (name, age) VALUES (?, ?)", "Tom", 25);
流程图 :
graph TD
A[开始] --> B[获取数据库连接]
B --> C[创建PreparedStatement]
C --> D[设置参数]
D --> E[执行SQL更新]
E --> F[返回受影响行数]
F --> G[自动关闭资源]
G --> H[结束]
4.2.2 查询操作的泛型处理与结果映射
为实现通用查询操作,我们可以使用泛型来支持不同的结果类型,并通过接口回调的方式处理 ResultSet 。
@FunctionalInterface
public interface RowMapper<T> {
T mapRow(ResultSet rs) throws SQLException;
}
public class JdbcUtils {
private DruidDataSource dataSource;
public JdbcUtils(DruidDataSource dataSource) {
this.dataSource = dataSource;
}
public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... params) {
List<T> result = new ArrayList<>();
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
for (int i = 0; i < params.length; i++) {
ps.setObject(i + 1, params[i]);
}
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
result.add(rowMapper.mapRow(rs));
}
}
} catch (SQLException e) {
throw new RuntimeException("执行SQL查询失败", e);
}
return result;
}
}
参数说明 :
-sql:带参数占位符的查询语句。
-rowMapper:用于将ResultSet映射为对象。
-params:查询参数。使用示例 :
List<User> users = jdbcUtils.query(
"SELECT id, name, age FROM users WHERE age > ?",
rs -> new User(rs.getInt("id"), rs.getString("name"), rs.getInt("age")),
20
);
表格:RowMapper 与 Lambda 表达式映射对比
| 特性 | RowMapper 接口方式 | Lambda 表达式方式 |
|---|---|---|
| 可读性 | 高 | 高 |
| 复用性 | 高 | 低 |
| 实现方式 | 接口实现 | Lambda 表达式 |
| 适用场景 | 通用映射 | 一次性查询 |
| 代码简洁性 | 一般 | 高 |
4.3 事务管理与连接复用
4.3.1 事务控制的基本流程
在数据库操作中,事务用于保证一组操作的原子性、一致性、隔离性和持久性(ACID)。Druid 提供了对事务的支持,可以通过 Connection 手动控制事务。
public void transferMoney(String fromUser, String toUser, double amount) {
try (Connection conn = dataSource.getConnection()) {
try {
conn.setAutoCommit(false); // 关闭自动提交
// 扣除转账金额
String deductSql = "UPDATE users SET balance = balance - ? WHERE name = ?";
try (PreparedStatement ps = conn.prepareStatement(deductSql)) {
ps.setDouble(1, amount);
ps.setString(2, fromUser);
ps.executeUpdate();
}
// 增加收款金额
String addSql = "UPDATE users SET balance = balance + ? WHERE name = ?";
try (PreparedStatement ps = conn.prepareStatement(addSql)) {
ps.setDouble(1, amount);
ps.setString(2, toUser);
ps.executeUpdate();
}
conn.commit(); // 提交事务
} catch (SQLException e) {
conn.rollback(); // 回滚事务
throw new RuntimeException("转账失败", e);
}
} catch (SQLException e) {
throw new RuntimeException("获取数据库连接失败", e);
}
}
代码逻辑分析 :
- 使用conn.setAutoCommit(false)关闭自动提交,开启事务。
- 使用conn.commit()提交事务。
- 在异常处理中使用conn.rollback()回滚事务。
- 所有数据库操作共享同一个连接,确保事务一致性。
4.3.2 使用Druid实现事务隔离与回滚
Druid 允许开发者在获取连接时指定事务的隔离级别,以满足不同业务场景的需求。例如,设置为 Connection.TRANSACTION_READ_COMMITTED 。
public void transferWithIsolation() {
try (Connection conn = dataSource.getConnection()) {
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
conn.setAutoCommit(false);
// 业务操作...
conn.commit();
} catch (SQLException e) {
// 回滚
}
}
事务隔离级别说明 :
| 隔离级别 | 常量值 | 描述 |
|----------|--------|------|
| 读未提交 | TRANSACTION_READ_UNCOMMITTED | 可读取未提交数据,存在脏读 |
| 读已提交 | TRANSACTION_READ_COMMITTED | 可读取已提交数据,防止脏读 |
| 可重复读 | TRANSACTION_REPEATABLE_READ | 防止脏读、不可重复读 |
| 串行化 | TRANSACTION_SERIALIZABLE | 防止所有并发问题 |
4.4 异常处理与资源释放机制
4.4.1 数据库连接泄漏问题的排查
数据库连接泄漏是指连接使用后未被释放,导致连接池资源耗尽。Druid 提供了连接监控功能,可以设置连接超时时间、开启监控日志等手段排查连接泄漏问题。
# application.yml 示例配置
spring:
datasource:
druid:
filters: stat
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
test-while-idle: false
test-on-borrow: true
validation-query: SELECT 1
max-active: 20
remove-abandoned: true
remove-abandoned-timeout: 180
log-abandoned: true
关键配置说明 :
-remove-abandoned: 是否开启连接泄漏回收。
-remove-abandoned-timeout: 连接超时回收时间(秒)。
-log-abandoned: 是否记录泄漏连接的堆栈信息。
4.4.2 使用try-with-resources管理资源
Java 7 引入的 try-with-resources 语法可以自动关闭实现了 AutoCloseable 接口的资源,极大简化了资源释放逻辑。
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users");
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}
优势 :
- 自动关闭资源,无需手动调用close()。
- 避免因异常跳过资源关闭导致的内存泄漏。
- 代码更简洁,可读性更高。注意事项 :
- 所有需要关闭的资源必须在try括号中声明。
- 资源顺序决定关闭顺序(后声明的先关闭)。章节总结 :
第四章从 JDBC 基础操作出发,逐步引导读者使用 Druid 实现数据库增删改查操作的封装。通过统一接口封装、泛型映射、事务管理、资源自动释放等实践,帮助开发者构建高效、安全、可维护的数据库访问层。下一章将继续深入,讲解如何配置和使用 Druid 的监控功能。
5. Druid监控功能配置与访问(webStatFilter、statViewServlet)
Druid 不仅是一个高性能的数据库连接池,还内置了强大的监控和统计功能。通过其监控模块,开发者可以实时查看 SQL 执行情况、连接池状态、慢 SQL 检测、Web 请求统计等关键指标。本章将从监控功能的核心模块入手,逐步讲解如何配置 statViewServlet 与 webStatFilter ,并通过实际示例展示如何在 Spring Boot 项目中集成 Druid 的监控页面,最终实现与 Spring Boot Admin 的整合,达到集中可视化监控的效果。
5.1 监控模块的核心功能概述
Druid 的监控模块是其区别于其他连接池的重要特性之一。它通过内置的 Servlet 和 Filter 实现对数据库访问的全面监控。主要功能包括:
- SQL执行统计 :记录每条 SQL 的执行次数、平均耗时、最大耗时等。
- 慢 SQL 检测 :自动识别执行时间超过阈值的 SQL。
- 连接池状态监控 :显示当前连接池的连接数、活跃连接数、空闲连接数等。
- Web请求监控 :记录页面访问次数、Session数量、请求路径等。
- 安全访问控制 :支持用户名密码登录,防止未授权访问。
功能对比表
| 功能模块 | 说明 | 是否支持 |
|---|---|---|
| SQL执行统计 | 统计所有SQL的执行频率与耗时 | ✅ |
| 慢SQL检测 | 可配置慢SQL阈值,自动记录 | ✅ |
| 连接池状态监控 | 实时展示连接池运行状态 | ✅ |
| Web请求统计 | 支持过滤器统计页面访问 | ✅ |
| 安全访问控制 | 可配置登录账号密码 | ✅ |
这些功能使得 Druid 不仅是一个连接池,更是一个数据库操作的监控平台。
5.2 配置Druid内置监控页面
Druid 提供了一个基于 Servlet 的监控页面,通过配置 statViewServlet 可以访问这个页面。
5.2.1 配置 statViewServlet 实现监控页面访问
在 Spring Boot 项目中,我们可以通过 Java 配置类来注册这个 Servlet。
示例代码
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceBuilder;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(DruidDataSourceProperties.class)
public class DruidConfig {
@Bean
public ServletRegistrationBean<com.alibaba.druid.support.http.StatViewServlet> statViewServlet() {
ServletRegistrationBean<com.alibaba.druid.support.http.StatViewServlet> bean =
new ServletRegistrationBean<>(new com.alibaba.druid.support.http.StatViewServlet(), "/druid/*");
// 设置访问控制的账号密码
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin"); // 登录用户名
initParams.put("loginPassword", "admin"); // 登录密码
initParams.put("allow", ""); // 允许所有IP访问,生产环境应设置具体IP
bean.setInitParameters(initParams);
return bean;
}
}
代码逻辑分析
- ServletRegistrationBean :用于将
StatViewServlet注册为 Web 应用的 Servlet。 - loginUsername 与 loginPassword :用于设置访问监控页面的账号密码,防止未授权访问。
- allow :允许访问的 IP 地址,设置为空表示允许所有 IP 访问(测试环境可用,生产环境建议配置白名单)。
参数说明
| 参数名 | 含义 | 示例值 |
|---|---|---|
| loginUsername | 登录用户名 | “admin” |
| loginPassword | 登录密码 | “admin” |
| allow | 允许访问的IP | “192.168.1.1,127.0.0.1” |
5.2.2 配置 login-username 与 login-password 实现权限控制
在 application.yml 中也可以通过配置方式设置登录账号密码:
spring:
datasource:
druid:
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: admin
reset-enable: false # 关闭重置功能
配置说明
-
login-username:监控页面的登录用户名。 -
login-password:监控页面的登录密码。 -
reset-enable:是否允许重置数据源,建议生产环境关闭。
5.3 配置 webStatFilter 进行 Web 请求监控
webStatFilter 是 Druid 提供的用于统计 Web 请求的过滤器组件。通过它,我们可以监控页面访问量、Session 数量、资源加载情况等。
5.3.1 配置 url-pattern 与排除资源
在 Java 配置类中添加如下代码:
@Bean
public FilterRegistrationBean<com.alibaba.druid.support.http.WebStatFilter> webStatFilter() {
FilterRegistrationBean<com.alibaba.druid.support.http.WebStatFilter> bean =
new FilterRegistrationBean<>(new com.alibaba.druid.support.http.WebStatFilter());
// 配置过滤规则
bean.addUrlPatterns("/*");
// 忽略资源
bean.addInitParameter("exclusions", "*.js,*.css,/druid/*");
return bean;
}
代码逻辑分析
- addUrlPatterns(“/*”) :表示监控所有请求路径。
- exclusions :配置不监控的资源,避免静态资源影响统计。
- url-pattern :与 Web 请求匹配的路径。
参数说明
| 参数名 | 含义 | 示例值 |
|---|---|---|
| exclusions | 排除监控的资源路径 | “ .js, .css,/druid/*” |
| url-pattern | 要监控的URL路径 | ”/*” |
5.3.2 统计页面访问次数与 Session 数量
webStatFilter 支持自动统计以下内容:
- 页面访问次数(PV)
- 独立访客(UV)
- Session 数量
- 资源加载时间等
这些数据可以在 Druid 的监控页面中实时查看。
Mermaid 流程图:Web 请求监控流程
graph TD
A[客户端发起请求] --> B[经过webStatFilter]
B --> C{是否匹配url-pattern?}
C -->|是| D[统计PV/UV/Session]
C -->|否| E[忽略统计]
D --> F[记录日志]
E --> G[直接放行]
5.4 集成 Spring Boot Admin 进行可视化监控
Spring Boot Admin 是一个用于集中管理 Spring Boot 应用的可视化监控平台。通过集成 Druid 与 Spring Boot Admin,可以实现对多个应用的连接池状态、SQL 执行情况等进行统一展示。
5.4.1 Spring Boot Admin 与 Druid 整合方案
步骤 1:引入依赖
在 pom.xml 中添加 Spring Boot Admin 客户端依赖:
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.7.4</version>
</dependency>
步骤 2:配置 Spring Boot Admin 地址
spring:
boot:
admin:
client:
url: http://localhost:8080
application:
name: druid-monitor-app
步据 3:确保 Druid 的监控端点开放
management:
endpoints:
web:
exposure:
include: "*"
5.4.2 实现监控信息的集中展示
通过 Spring Boot Admin 的 Web 页面,可以查看每个应用的 Druid 监控信息,包括:
- 当前连接池大小
- 活跃连接数
- SQL 执行次数与耗时
- 慢 SQL 列表
示例截图说明(文字模拟)
Spring Boot Admin 控制台
├── 应用列表
│ └── druid-monitor-app
│ ├── 连接池状态
│ │ ├── 初始连接数:5
│ │ ├── 最大连接数:20
│ │ ├── 当前活跃连接:3
│ │ └── 空闲连接:17
│ └── SQL 监控
│ ├── 执行次数最多的SQL:SELECT * FROM user
│ └── 慢SQL列表:SELECT * FROM orders WHERE create_time < '2020-01-01'
└── 健康状态
小结与拓展
本章详细讲解了如何配置和使用 Druid 的监控模块,包括:
-
statViewServlet的配置与访问控制 -
webStatFilter的使用与 Web 请求监控 - 与 Spring Boot Admin 的整合,实现可视化集中监控
Druid 的监控功能不仅能帮助开发者及时发现性能瓶颈,还能为生产环境的数据库调优提供重要依据。下一章我们将深入探讨 Druid 的 SQL 解析机制与 SQL 注入防护,进一步提升系统的安全性和可控性。
6. SQL解析与注入风险检测机制
SQL注入是一种常见的Web安全漏洞,攻击者通过在输入中注入恶意SQL代码,绕过应用程序的逻辑控制,从而非法获取或篡改数据库数据。Druid不仅是一个高性能的数据库连接池,还内置了强大的SQL解析引擎,能够有效识别和防范SQL注入行为。本章将深入探讨SQL注入的原理、Druid的SQL解析机制以及如何利用其内置能力实现SQL注入防护。
6.1 SQL注入攻击原理与防护机制
SQL注入攻击的本质在于攻击者通过构造特殊的输入,使得原本预期的SQL语句结构被改变,从而执行非预期的SQL命令。
6.1.1 SQL注入攻击的常见手段
常见的SQL注入攻击方式包括:
| 攻击方式 | 描述 | 示例 |
|---|---|---|
| 布尔盲注 | 通过返回真假判断数据库内容 | ' OR '1'='1 |
| 时间盲注 | 利用延迟响应判断数据库内容 | ' AND IF(1=1, SLEEP(5), 0)-- |
| 联合查询注入 | 通过 UNION SELECT 窃取数据 | ' UNION SELECT username, password FROM users-- |
| 错误注入 | 通过构造错误语句获取数据库结构 | ' AND 1=CONVERT(int, (SELECT @@version))-- |
这些攻击方式的核心思想是: 通过字符串拼接的方式构建SQL语句时,未对输入进行有效过滤或参数化处理。
6.1.2 PreparedStatement与参数化查询的作用
Java中使用 PreparedStatement 可以有效防止SQL注入。与 Statement 不同, PreparedStatement 允许开发者将SQL语句中的变量作为参数传入,而不是直接拼接字符串。
String query = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, username);
stmt.setString(2, password);
在这个例子中,即使 username 或 password 中包含恶意字符串,也不会改变SQL语句的结构,因为参数被当作值处理,而非SQL逻辑的一部分。
6.2 Druid的SQL解析引擎
Druid不仅是一个连接池,其内置的SQL解析器能够对SQL语句进行结构化分析,为SQL注入检测、日志分析、审计等功能提供基础。
6.2.1 SQLParser的基本结构与使用方式
Druid的SQL解析器位于 com.alibaba.druid.sql 包中,支持多种数据库方言(MySQL、Oracle、PostgreSQL等)。通过解析器,可以将SQL语句转换为抽象语法树(AST),从而进行语义分析。
以下是一个简单的SQL解析示例:
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.parser.SQLParserUtils;
import com.alibaba.druid.sql.parser.SQLStatementParser;
import java.util.List;
public class SQLParserExample {
public static void main(String[] args) {
String dbType = "mysql";
String sql = "SELECT * FROM users WHERE username = 'admin' OR '1'='1';";
// 创建SQL解析器
SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType);
// 解析SQL语句
List<SQLStatement> stmtList = parser.parseStatementList();
// 输出AST结构
for (SQLStatement stmt : stmtList) {
System.out.println(SQLUtils.toSQLString(stmt));
}
}
}
代码解释:
-
SQLParserUtils.createSQLStatementParser(sql, dbType):根据数据库类型创建对应的解析器。 -
parser.parseStatementList():将SQL语句解析为多个SQLStatement对象。 -
SQLUtils.toSQLString(stmt):将AST结构重新转换为标准SQL字符串。
逻辑分析:
该代码展示了如何使用Druid的SQL解析器将SQL字符串转换为结构化的AST对象。通过AST结构,可以进一步分析SQL的语法结构,如是否包含 UNION 、 OR 等敏感关键字。
6.2.2 解析SQL语句的AST结构
AST(Abstract Syntax Tree)是SQL解析器生成的语法树结构,它将SQL语句拆解为多个节点,便于后续分析。例如,SQL语句:
SELECT * FROM users WHERE username = 'admin' OR '1'='1';
其AST结构可能如下:
graph TD
A[SQLSelectStatement] --> B[SELECT]
A --> C[FROM users]
A --> D[WHERE]
D --> E[username = 'admin']
D --> F[OR]
F --> G['1'='1']
通过AST分析,可以识别出 WHERE 子句中是否存在逻辑运算符(如 OR 、 AND )以及是否包含常量比较(如 '1'='1' ),这些往往是SQL注入的特征。
6.3 检测SQL注入风险的实现机制
Druid通过SQL解析器和内置规则引擎,可以对SQL语句进行模式匹配,识别潜在的注入风险。
6.3.1 敏感关键字检测
Druid可以通过检测SQL中是否包含以下敏感关键字来判断是否为注入行为:
-
UNION -
OR -
AND -
DROP -
DELETE -
EXEC -
SLEEP -
WAITFOR
实现方式如下:
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.parser.SQLParserUtils;
import com.alibaba.druid.sql.parser.SQLStatementParser;
import java.util.List;
public class SQLInjectionDetector {
private static final List<String> SENSITIVE_KEYWORDS = List.of("UNION", "OR", "AND", "DROP", "DELETE");
public static boolean isPotentialSQLInjection(String sql) {
try {
SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, "mysql");
List<SQLStatement> stmtList = parser.parseStatementList();
for (SQLStatement stmt : stmtList) {
String stmtStr = SQLUtils.toSQLString(stmt).toUpperCase();
for (String keyword : SENSITIVE_KEYWORDS) {
if (stmtStr.contains(keyword)) {
System.out.println("敏感关键字检测到:" + keyword);
return true;
}
}
}
} catch (Exception e) {
System.err.println("SQL解析失败:" + e.getMessage());
return true; // 解析失败也视为风险
}
return false;
}
public static void main(String[] args) {
String testSQL = "SELECT * FROM users WHERE username = 'admin' OR '1'='1';";
if (isPotentialSQLInjection(testSQL)) {
System.out.println("该SQL存在注入风险");
} else {
System.out.println("该SQL安全");
}
}
}
逻辑分析:
- 使用
SQLUtils.toSQLString(stmt).toUpperCase()将SQL语句统一转为大写,便于关键字匹配。 - 对解析后的SQL语句进行敏感关键字检测。
- 若检测到关键字或解析失败,返回
true表示存在风险。
6.3.2 SQL语法树的模式匹配
除了关键字检测,还可以通过AST结构分析SQL逻辑结构。例如,检测是否包含 WHERE 子句中多个条件的组合,或是否包含恒真条件(如 '1'='1' )。
// 省略导入和类定义
public static boolean hasAlwaysTrueCondition(SQLStatement stmt) {
String sql = SQLUtils.toSQLString(stmt);
return sql.contains("'1'='1'") || sql.contains("1=1");
}
public static boolean detectAdvancedSQLInjection(String sql) {
SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, "mysql");
List<SQLStatement> stmtList = parser.parseStatementList();
for (SQLStatement stmt : stmtList) {
if (hasAlwaysTrueCondition(stmt)) {
System.out.println("检测到恒真条件");
return true;
}
}
return false;
}
参数说明:
-
hasAlwaysTrueCondition(stmt):检测SQL中是否包含'1'='1'或1=1等恒真条件。 - 该方法结合AST结构和字符串匹配,提高检测准确性。
6.4 结合WAF实现SQL注入防护
在实际生产环境中,仅靠SQL解析器难以覆盖所有攻击手段,因此需要将Druid与Web应用防火墙(WAF)结合,形成多层防护体系。
6.4.1 整合Druid SQL解析器与Web应用防火墙
WAF通常部署在应用前端,负责对所有请求进行预处理和过滤。结合Druid的SQL解析能力,可以在请求到达业务层之前进行SQL注入检测。
示例:Spring Boot中整合Druid SQL解析器作为WAF拦截器
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class SQLInjectionWAFInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String query = request.getParameter("query");
if (query != null && SQLInjectionDetector.isPotentialSQLInjection(query)) {
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "SQL注入风险检测到");
return false;
}
return true;
}
}
逻辑分析:
- 该拦截器在Spring Boot中拦截所有请求。
- 对参数
query进行SQL注入检测。 - 若检测到风险,返回400错误并阻断请求。
6.4.2 日志记录与攻击响应机制
在实际部署中,建议将SQL注入尝试记录到日志系统中,并触发安全告警。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SQLInjectionLogger {
private static final Logger logger = LoggerFactory.getLogger(SQLInjectionLogger.class);
public static void logInjectionAttempt(String sql, String remoteAddr) {
logger.warn("SQL注入尝试检测到: {} 来自IP: {}", sql, remoteAddr);
// 可扩展为发送告警邮件、写入安全日志等
}
}
使用方式:
在检测到注入风险时调用日志记录方法:
if (SQLInjectionDetector.isPotentialSQLInjection(sql)) {
SQLInjectionLogger.logInjectionAttempt(sql, request.getRemoteAddr());
response.sendError(HttpServletResponse.SC_FORBIDDEN, "SQL注入行为已记录");
}
通过本章内容,我们从SQL注入的基本原理出发,介绍了Druid的SQL解析能力,并结合实际代码展示了如何检测SQL注入风险。最后,我们还探讨了如何将Druid的解析能力与WAF结合,实现对Web应用的全方位防护。
7. 自定义SQL拦截器实现日志打印与执行控制
7.1 拦截器机制与Druid的Filter体系
Druid通过Filter机制提供了强大的拦截与扩展能力。Filter是一种插件式结构,可以在SQL执行前后进行拦截处理,支持日志记录、性能监控、SQL改写、访问控制等多种功能。
7.1.1 Filter的执行流程与作用范围
Druid的Filter体系采用责任链模式(Chain of Responsibility),多个Filter可以串联执行。每个Filter都有机会在SQL执行前后进行干预。
- Filter执行流程图 :
graph TD
A[SQL执行请求] --> B(Filter1前置处理)
B --> C(Filter2前置处理)
C --> D[执行SQL]
D --> E(Filter2后置处理)
E --> F(Filter1后置处理)
F --> G[返回结果]
Filter作用范围包括:
- DataSource级 :作用于整个数据源的SQL操作。
- Connection级 :作用于单个数据库连接。
- Statement级 :作用于SQL语句执行过程。
- ResultSet级 :作用于结果集读取阶段。
7.1.2 内置Filter的功能与配置方式
Druid内置了多个Filter,如:
- stat :用于监控SQL执行性能。
- wall :用于防止SQL注入攻击。
- log4j / slf4j / common logging :用于记录SQL日志。
在Spring Boot项目中,可通过配置启用内置Filter:
spring:
datasource:
druid:
filter:
enabled: true
stat:
enabled: true
wall:
enabled: true
slf4j:
enabled: true
7.2 实现自定义SQL拦截器
自定义Filter可以实现对SQL的深度控制,如记录执行时间、打印SQL语句、统计执行次数等。
7.2.1 编写Filter实现SQL日志打印
继承 com.alibaba.druid.filter.FilterEventAdapter 类,重写 statementExecuteBefore 和 statementExecuteAfter 方法。
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.stat.JdbcSqlStat;
import java.util.Properties;
public class CustomSqlLogFilter extends FilterEventAdapter {
@Override
public void statementExecuteBefore(com.alibaba.druid.filter.FilterChain chain,
Object connection,
Object statement,
String sql) {
// 记录开始时间
long startTime = System.currentTimeMillis();
chain.setAttribute(statement, "start_time", startTime);
}
@Override
public void statementExecuteAfter(com.alibaba.druid.filter.FilterChain chain,
Object connection,
Object statement,
String sql,
Throwable error) {
long endTime = System.currentTimeMillis();
Long startTime = (Long) chain.getAttribute(statement, "start_time");
long duration = endTime - (startTime == null ? endTime : startTime);
// 打印SQL及执行时间
System.out.println("SQL: " + sql);
System.out.println("Execution Time: " + duration + " ms");
}
@Override
public void init(Properties properties) {
// 可以读取配置参数
}
}
7.2.2 记录执行时间与SQL语句内容
上面的示例中, statementExecuteBefore 记录了SQL执行前的时间戳, statementExecuteAfter 在执行结束后计算耗时,并输出SQL语句和执行时间。开发者可以将日志输出改为使用日志框架(如SLF4J)记录,便于后续日志分析。
7.3 控制SQL执行行为
通过Filter不仅可以记录日志,还可以对SQL执行行为进行控制,如设置执行超时限制、拦截非法SQL等。
7.3.1 限制执行时间超过阈值的SQL
可以在 statementExecuteAfter 中加入判断逻辑:
if (duration > 1000) {
System.warn("SQL 超时警告: " + sql + ",耗时:" + duration + " ms");
// 可抛出异常中断执行或记录告警
}
7.3.2 拦截非法SQL语句并抛出异常
例如,拦截包含 DROP 或 DELETE 的SQL:
if (sql.toUpperCase().contains("DROP") || sql.toUpperCase().contains("DELETE")) {
throw new RuntimeException("非法SQL操作被拦截: " + sql);
}
这可以作为轻量级SQL防护机制,防止误操作或攻击行为。
7.4 拦截器在分布式系统中的应用
在微服务架构中,多个服务共享数据库资源时,SQL拦截器可用于实现统一的监控与日志管理。
7.4.1 跨服务SQL日志聚合分析
将拦截器输出的日志统一发送到日志收集系统(如ELK、SLS、Graylog),可实现跨服务SQL日志的集中分析。
示例(使用Logback):
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(CustomSqlLogFilter.class);
// 替换 System.out 输出为日志记录
logger.info("SQL: {} | Time: {} ms", sql, duration);
日志示例:
INFO [CustomSqlLogFilter] SQL: SELECT * FROM users WHERE id = ? | Time: 45 ms
7.4.2 微服务架构下的统一SQL监控
在多个服务中部署相同的Filter逻辑,可以统一SQL执行监控策略。例如:
- 所有服务均启用SQL慢查询监控。
- 统一拦截DELETE语句并记录审计日志。
- 与链路追踪系统(如SkyWalking、Zipkin)集成,记录SQL在调用链中的位置与耗时。
通过Druid拦截器机制,开发者可以构建出一套完善的SQL执行监控体系,为系统的可观测性、安全性和运维能力提供强有力支撑。
简介:Druid是阿里巴巴开发的一款高性能Java数据库连接池组件,具备连接管理、性能监控、SQL解析、安全过滤和健康检查等功能。本文围绕Druid连接池的配置与使用展开,涵盖Maven依赖引入、Spring Boot配置方式、数据库工具类封装、监控页面设置以及SQL安全防护等内容,帮助开发者掌握Druid在实际项目中的应用,提升数据库访问效率与系统稳定性。
Druid连接池实战与应用
1392

被折叠的 条评论
为什么被折叠?



