1. 动机
很多优良的框架都用到 filter,之前的意识比拟含糊,心愿本次有所突破。
2.demo
先入手写一个简略的 demo
@Component
public class MyFilter extends FilterEventAdapter {
@Override
protected void statementExecuteBefore(StatementProxy statement, String sql){System.out.println(sql + "----------MyFilter-------- 执行开始前!------");
super.statementExecuteBefore(statement,sql);
}
@Override
protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {System.out.println(sql + "----------MyFilter-------- 执行完结后!------");
super.statementExecuteAfter(statement, sql, result);
}
}
继承 FilterEventAdapter,复写俩个办法,打两段日志,代码非常简单。执行测试用例:
@Test
void insertTest() {String sql = "insert into t_druid_test (id,firstname)" +
"VALUES (1,'JINX')";
jdbcTemplate.execute(sql);
}
后果如下:
一个简略的自定义 filter 已实现
3. 解析
3.1 Filter
先看 filter 类图
呃,太长了,看不了 … 总结一下,针对 connection_xxx,resultSet_xxx,statement_xxx,preparedStatement_xxx,callableStatement_xxx,另外还有 init,destroy 等办法,根本参加了 JDBC 的全生命周期(datasource->connection->(Prepared)Statement->resultSet),果然是为监控而生。
抽象类 FilterAdapter
实现了 filter 接口,提供了根本的实现,大大减少了反复编码。
FilterEventAdapter
继承FilterAdapter
, 在 filter 的根底上新增了 xxxx_before,xxxx_after 办法,能够用来做更多的事。
3.2 FilterChain
看类图,接口办法根本和 filter 统一,次要承当串联 filter 和传递调用事件的职责。该接口的实现类 FilterChainImpl,轻易找一个办法实现看一下:
public ConnectionProxy connection_connect(Properties info) throws SQLException {
// 将调用事件一直往下传递,直至 this.pos == filterSize
if (this.pos < filterSize) {return nextFilter()
.connection_connect(this, info);
}
// 本办法的职责解决
Driver driver = dataSource.getRawDriver();
String url = dataSource.getRawJdbcUrl();
Connection nativeConnection = driver.connect(url, info);
if (nativeConnection == null) {return null;}
return new ConnectionProxyImpl(dataSource, nativeConnection, info, dataSource.createConnectionId());
}
此类的办法根本都是这种构造,分为传递调用和本办法的职责解决。
4.filter 如何工作
4.1 初始化
DruidDataSource 类 init 办法中有上面一段代码
for (Filter filter : filters) {filter.init(this);
}
4.2 如何工作
对于配置的 filter 以及自定义的 filter 都会在此初始化,如果有须要能够持有该 datasource 的援用。再看一下 getConnection 办法:
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {init();
// init 初始化后,filters 不会为空
if (filters.size() > 0) {
// 初始化 chain
FilterChainImpl filterChain = new FilterChainImpl(this);
return filterChain.dataSource_connect(this, maxWaitMillis);
} else {return getConnectionDirect(maxWaitMillis);
}
}
能够看到,每次调用链中的办法,链总是在调用时初始化。再贴一下 connection_connect 办法
public DruidPooledConnection dataSource_connect(DruidDataSource dataSource, long maxWaitMillis) throws SQLException {
// 将调用事件一直往下传递,直至 this.pos == filterSize
if (this.pos < filterSize) {DruidPooledConnection conn = nextFilter().dataSource_getConnection(this, dataSource, maxWaitMillis);
return conn;
}
// 本职工作,获取 Connection
return dataSource.getConnectionDirect(maxWaitMillis);
}
private Filter nextFilter() {return getFilters()
.get(pos++);// 留神这里 pos++
}
FilterAdapter 类的 dataSource_getConnection 办法
public DruidPooledConnection dataSource_getConnection(FilterChain chain, DruidDataSource dataSource,
long maxWaitMillis) throws SQLException {return chain.dataSource_connect(dataSource, maxWaitMillis);// 将调用关系转接给 chain 对象,又回到下面的 dataSource_connect 办法
}
贴一下调试截图
- 当 this.pos == filterSize 时,会执行真正的逻辑
-
chain 通过持有的的 Filter 对象去调用真正的办法,Filter 对象执行时,又将调用关系转接给 chain,这样就能一直推动上来
流程图如下:5. 参考链接
druid 源码钻研之 Filter: https://blog.csdn.net/lqzkcx3…
druid 源码剖析与学习(含具体监控设计思路的彩蛋: https://www.iteye.com/blog/he…