跳转到内容

JDBC 代理与过滤器链

Druid 的过滤器不是 Servlet 式的请求拦截器,而是按 JDBC 方法分别实现的责任链。每个链方法都有明确的末端行为,因此能覆盖连接、语句和结果集的不同生命周期。

先给答案:Druid 的过滤器不是 Servlet 式的请求拦截器,而是按 JDBC 方法分别实现的责任链

Section titled “先给答案:Druid 的过滤器不是 Servlet 式的请求拦截器,而是按 JDBC 方法分别实现的责任链”

Druid 的过滤器不是 Servlet 式的请求拦截器,而是按 JDBC 方法分别实现的责任链。每个链方法都有明确的末端行为,因此能覆盖连接、语句和结果集的不同生命周期。 正文沿“代理层 -> 链推进 -> 为什么让每个 JDBC 事件拥有独立链方法”展开:先确认入口和状态归属,再跟踪控制流或数据流的推进,最后落到对外可观察的结果。

主要失效边界集中在“忘记继续链、重复执行、只拦 execute”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。

  • ConnectionProxyImpl:proxy/jdbc/ConnectionProxyImpl.java:30。
  • 过滤器链字段与初始化::43-88。
  • createStatement::166-182。
  • prepareStatement::358-371。
  • StatementProxyImpl:proxy/jdbc/StatementProxyImpl.java:30。
  • execute:StatementProxyImpl.java:135-143。
  • executeQuery::208-219。
  • executeUpdate::223-231。

Filter 定义事件协议:connection_connect 在 filter/Filter.java:46,resultSet_next 在 :178,statement_executeQuery 在 :678,statement_execute 在 :706,dataSource_getConnection 在 :1313。

FilterChain 声明同名链方法:filter/FilterChain.java:48、:170、:589、:617、:1313。FilterChainImpl#nextFilter 在 FilterChainImpl.java:469 负责沿 pos < filterSize 取下一个过滤器,链尾才执行 raw JDBC。

为什么让每个 JDBC 事件拥有独立链方法

Section titled “为什么让每个 JDBC 事件拥有独立链方法”

替代方案:定义一个通用 aroundInvoke(methodName, args),所有逻辑通过反射分派。 为什么不行:JDBC 方法参数和返回值差异很大,反射会损失类型安全、调试可读性和热路径性能。 证据:Filter、FilterChain 和 FilterChainImpl 对 execute、executeQuery、resultSet_next 分别提供静态签名。

过滤器通常形成“前置 -> 下游 -> 后置”的结构。Wall 在执行前检查 SQL,Stat 在前置阶段记录开始时间,在下游返回后计算耗时;自定义 Filter 插入错误位置会改变观测结果。

场景 现象 原因 规避
忘记继续链 SQL 不再执行 Filter 没有调用 chain 方法 明确前置、下游、后置三段
重复执行 一次业务出现两条 SQL 过滤器重复调用 next 每个事件只推进一次
只拦 execute prepare 场景漏统计 JDBC 调用入口不同 同时覆盖 query/update/execute
ResultSet 漏关 连接无法归还 只监听 Statement 监听 ResultSet close/next

当 API 方法族稳定且类型差异明显时,显式链方法比万能反射拦截更适合性能敏感代码。协议清晰也更容易让安全和统计插件独立演进。

面试锚点

  • FilterChain 的 pos 如何保证每个过滤器只执行一次?
  • 为什么代理需要覆盖 ResultSet,而不是只代理 Connection?
  • WallFilter 和 StatFilter 的顺序会影响什么?