Druid 中的filter
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++
}
【Druid 中的filter】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...
推荐阅读
- 热闹中的孤独
- JS中的各种宽高度定义及其应用
- 我眼中的佛系经纪人
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- Android中的AES加密-下
- 放下心中的偶像包袱吧
- C语言字符函数中的isalnum()和iscntrl()你都知道吗
- C语言浮点函数中的modf和fmod详解
- C语言中的时间函数clock()和time()你都了解吗
- 如何在Mac中的文件选择框中打开系统隐藏文件夹