1) 能够监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,可以详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。 node
2) 替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库链接池。 git
3) 数据库密码加密。直接把数据库密码写在配置文件中,这是很差的行为,容易致使安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。 github
4) SQL执行日志,Druid提供了不一样的LogFilter,可以支持Common-Logging、Log4j和JdkLog,你能够按须要选择相应的LogFilter,监控你应用的数据库访问状况。 sql
扩展JDBC,若是你要对JDBC层有编程的需求,能够经过Druid提供的Filter-Chain机制,很方便编写JDBC层的扩展插件。 数据库
-------------------------------------分割线--------------------------------------------编程
这才是本篇文章重点要描述的。安全
https://github.com/alibaba/druid/wiki/%E9%A6%96%E9%A1%B5 druid官方里面好像没找到。ide
(代码里面的注释真的少得可怜。。。)函数
不过用过druid的知道里面内置了一些filter性能
例如在配置
<property name="filters" value="stat,log4j" />
附内置filter的别名
Filter类名 | 别名 |
default | com.alibaba.druid.filter.stat.StatFilter |
stat | com.alibaba.druid.filter.stat.StatFilter |
mergeStat | com.alibaba.druid.filter.stat.MergeStatFilter |
encoding | com.alibaba.druid.filter.encoding.EncodingConvertFilter |
log4j | com.alibaba.druid.filter.logging.Log4jFilter |
log4j2 | com.alibaba.druid.filter.logging.Log4j2Filter |
slf4j | com.alibaba.druid.filter.logging.Slf4jLogFilter |
commonlogging | com.alibaba.druid.filter.logging.CommonsLogFilter |
这些Filter都实现了com.alibaba.druid.filter.Filter 这个接口,里面的函数太多。。
其实最简单的方法就是仿照已有的Filter来写,例如Log4jFilter,
Log4jFilter 继承 LogFilter,从上面看出,Druid日志插件还支持Slf4jLog,CommonsLog。这里面其实就是根据不一样状况打印不一样的日志。
LogFilter又继承于FilterEventAdapter这个类,这个类好像很熟悉,貌似Spring里面常常会有xxxAdapter来帮咱们对一些接口实现一些默认的方法,而咱们只须要继承这个类, 重写里面须要重写方法就能够了。
里面最重要的方法就是
@Override public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException { try { //抽象方法 statementExecuteBefore(statement, statement.getSql()); boolean firstResult = chain.preparedStatement_execute(statement); //抽象方法,后面咱们会重写这个方法 this.statementExecuteAfter(statement, statement.getSql(), firstResult); return firstResult; } catch (SQLException error) { //抽象方法 statement_executeErrorAfter(statement, statement.getSql(), error); throw error; } catch (RuntimeException error) { //抽象方法 statement_executeErrorAfter(statement, statement.getSql(), error); throw error; } catch (Error error) { //抽象方法 statement_executeErrorAfter(statement, statement.getSql(), error); throw error; } }
好吧,这个方法就是实行FilterChain的preparedStatement_execute方法。这里就是filter的执行链上的一个node把。
1. 建立一个Filter
/** * Created by hzlizhou on 2017/2/5. */ public class TestFilter extends FilterEventAdapter{ @Override protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) { System.out.println("haha"); super.statementExecuteAfter(statement, sql, result); } }
咱们本身写一个TestFilter, 继承FilterEventAdapter,重写statementExecuteAfter方法(在SQL语句执行成功之后会执行)
2. 加入到执行链(ChainFilter)
仔细看DruidDataSource,他的父类,里面有个
protected List<Filter> filters = new CopyOnWriteArrayList<Filter>();
<property name="filters" value="stat,log4j" /> DataSource配置的时候就是调用这个set方法
public void setFilters(String filters) throws SQLException { if (filters != null && filters.startsWith("!")) { filters = filters.substring(1); this.clearFilters(); } this.addFilters(filters); }
还有一个set方法是直接加入filters
public void setProxyFilters(List<Filter> filters) { if (filters != null) { this.filters.addAll(filters); } }
所以,咱们在datasource配置中增长
<property name="proxyFilters"> <list> <ref bean="testFilter" /> </list> </property>
以及对应的bean
<bean id="testFilter" class="com.netease.urs.druid.filter.TestFilter"></bean>
运行程序,发现每次成功访问数据库都会打印"haha",看来这样作的是对的。这样,咱们就能够按照本身的需求自定义一些操做啦(个人目的其实就是数据库同步,把增、删、改的sql语句加入队列中)
简单分析下Druid的Filter吧。其实和通常的Filter同样的(Spring Security,Servlet),功能就是在执行真正的业务代码以前和以后增长一些自定义的功能。
顺着刚才的思路往上找,刚才分析到了
@Override public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException { try { //抽象方法 statementExecuteBefore(statement, statement.getSql()); //重点是这里!!!! boolean firstResult = chain.preparedStatement_execute(statement); //抽象方法,后面咱们会重写这个方法 this.statementExecuteAfter(statement, statement.getSql(), firstResult); return firstResult; } catch (SQLException error) { //抽象方法 statement_executeErrorAfter(statement, statement.getSql(), error); throw error; } catch (RuntimeException error) { //抽象方法 statement_executeErrorAfter(statement, statement.getSql(), error); throw error; } catch (Error error) { //抽象方法 statement_executeErrorAfter(statement, statement.getSql(), error); throw error; } }
先来看一下FilterChain ,他只有一个实现类FilterChainImpl,有3个成员变量,终于看到了DataSourceProxy(看名字也知道其实就是对DruidDataSource的代理类)
protected int pos = 0; private final DataSourceProxy dataSource; private final int filterSize;
preparedStatement_executeQuery的方法以下。这样在调用前会把全部的Filter都执行一次其中的preparedStatement_execute。
@Override public boolean preparedStatement_execute(PreparedStatementProxy statement) throws SQLException { if (this.pos < filterSize) { return nextFilter().preparedStatement_execute(this, statement); } return statement.getRawObject().execute(); }
preparedStatement_execute在哪里被执行的呢?PreparedStatementProxyImpl#execute()
PreparedStatementProxyImpl#execute()在哪里被执行的呢? DruidPooledPreparedStatement#execute()
(好吧,我认可我是debug看调用栈的。。DruidPooledPreparedStatement#execute()就是iBatis中的调用的了!)
调用的时序大概以下面
DruidPooledPreparedStatement除了execute(),还有executeUpdate(),executeQuery()。可是若是是基于ibatis的sql语句都是使用execute()。