mybatis支持不同的数据库,具体访问数据库是由不同的数据库厂商提供的驱动jar包。
比如:mysql的mysql-connector-java.jar
不同厂商提供的提供的jar也是基于JDK下的java.sql包中的接口去实现的
比如:不同的数据库厂商基于Connection接口实现了自家的Connection访问数据库类
不同厂商只要基于JDK提供的接口实现类,就能访问数据库
此处不得不赞美面向接口编程的精妙之处
mybatis框架的作用是把数据库的元数据转成java对象,方便java代码操作,具体的数据库访问操作由JDBC完成。但是很多时候我们想要看到mybaits和jdbc之间的交互痕迹,比如:mybatis调用了JDBC的哪些方法,传了什么参数,得到了什么执行结果。
如果打出这些sql日志方便我们bug排查和分析,也可以根据sql优化代码,比如:通过优化mybatis的缓存使用,减少数据访问次数,减少数据库压力,提高系统响应时间。
connection打印日志的代理类ConnectionLogger,这个命名有点误导,看命名不会把它当作是一个proxy,但是看到他实现了InvocationHandler接口就知道这是一个代理类了。
代理类持有一个connection接口,并不是具体的类,具体实现类看jdbc接入的是什么数据库的connection实现类,再次体现了面向接口编程的精妙之处😂,也体现了java多态的特性。
/**
* Connection proxy to add logging
* BaseJdbcLogger实现了几个代理的共同方法
*/
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
/**
* 动态代理的目标对象
*/
private final Connection connection;
private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.connection = conn;
}
@Override
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
// 返回拥有method方法的class
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
/**
* jdbc包下的这些类相当于装饰器
* 不过这里从装饰是把返回的类都装饰成了代理类
* 判断当前的方法是否需要代理
*/
if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
}
// 通过反射的方式调用代理类的代理方法
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
// PreparedStatement对返回值再次加工和封装,返回一个新的代理类
// 代理和被代理的类都需要实现同样的接口,这样就可以发挥java多态的特性了
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("createStatement".equals(method.getName())) {
Statement stmt = (Statement) method.invoke(connection, params);
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else {
return method.invoke(connection, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
/**
* Creates a logging version of a connection.
*
* 创建Connection代理的方法,注意这是一个static方法
*/
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
}
此段源码可以作为JDK动态代理和java面向接口编程的典型场景进行分析
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。