java中不能直接替换jdbc原生connection,但可通过jdk proxy动态代理connection接口,在preparestatement等方法调用时拦截并打印sql,再委托给真实连接;还需对创建的preparedstatement二次代理以记录执行耗时。

Java 中不能直接“替换” JDBC 原生的 Connection 实例,但可以通过反射 + 动态代理(如 JDK Proxy 或 CGLIB)在获取连接后,对其做一层包装代理,从而拦截 createStatement、prepareStatement 等方法,实现 SQL 日志输出。核心思路是:不修改原生 Connection,而是返回一个代理对象,在调用关键方法时记录 SQL 并委托给真实连接。
获取原始 Connection 后立即包装为代理
通常在 DataSource 的 getConnection() 返回后介入。例如在 Spring 中可配合 DataSourceProxy 或自定义 ConnectionCallback;若手动管理连接,则在 DriverManager.getConnection() 或 ds.getConnection() 之后立刻包装:
- 用
java.lang.reflect.Proxy.newProxyInstance()创建Connection接口的代理实例 - 代理的
InvocationHandler中拦截prepareStatement(String)、prepareStatement(String, int, int)等方法 - 对匹配的方法参数(SQL 字符串)进行日志打印,再将调用转发给原始 Connection
重点拦截 PreparedStatement 创建过程
真正执行 SQL 的是 PreparedStatement,所以仅代理 Connection 不够——还需对其创建出的 PreparedStatement 做二次代理:
- 在
InvocationHandler.invoke()中识别prepareStatement调用,提取 SQL 参数 - 调用原始 Connection 的
prepareStatement(...)得到真实 stmt - 用 Proxy 再包装该 stmt,使其
execute()、executeQuery()等方法也能记录 SQL 和执行耗时 - 注意:需同时代理
Statement和PreparedStatement,因有些框架或代码会直接用createStatement()
避免反射绕过访问限制的陷阱
某些 JDBC 驱动(如 Oracle、MySQL Connector/J 8+)内部使用非 public 类或 final 方法,直接反射调用可能失败。此时应:
- 优先使用接口代理(JDK Proxy),而非反射修改私有字段或调用私有方法
- 不要尝试通过反射替换
Connection对象本身(比如设入 DriverManager 内部缓存),这不可靠且易引发 ClassLoader 或并发问题 - 若需深度拦截(如获取 bind 参数值),可考虑使用字节码增强(Byte Buddy / Java Agent),但普通日志场景无需如此复杂
简单可运行的日志代理示例(JDK Proxy)
以下是一个最小可行的 Connection 代理片段(省略异常处理):
Connection proxyConn = (Connection) Proxy.newProxyInstance(
Connection.class.getClassLoader(),
new Class[]{Connection.class},
new InvocationHandler() {
private final Connection target;
public Handler(Connection target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if ("prepareStatement".equals(methodName) && args.length >= 1 && args[0] instanceof String) {
String sql = (String) args[0];
System.out.println("[SQL] " + sql.trim().replaceAll("\s+", " "));
}
return method.invoke(target, args);
}
}
);
实际项目中建议封装为工具类,并支持配置开关、SQL 截断、参数占位符标记等功能。Spring Boot 的 DataSourceProxy 或开源库如 p6spy、log4jdbc 已成熟实现该模式,可直接集成。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











