Mybatis源码分析之日志模块
mybatis没有提供第三方日志的实现,它默认提供了如下:
它提供了各种日志的适配器,(自己提供一套对外的统一日志接口,去对接第三方日志接口,将第三方日志接口适配到自己的统一接口中)适配器模式 可以自行百度,所有的日志通过Log接口统一访问,调用者无需关心内部系统的关系及实现
2.mybatis log的调用顺序
借鉴
MyBatis日志模块是怎么使用适配器模式?实现如下:
Target:目标角色,期待得到的接口。org.apache.ibatis.logging.Log 接口,对内提供了统一 的日志接口;
Adaptee:适配者角色,被适配的接口。其他日志组件组件如 slf4J 、commonsLoging 、 Log4J2 等被包含在适配器中。
Adapter:适配器角色,将源接口转换成目标接口。针对每个日志组件都提供了适配器, 每 个 适 配 器 都 对 特 定 的 日 志 组 件 进 行 封 装 和 转 换 ; 如 Slf4jLoggerImpl JakartaCommonsLoggingImpl 等; 日志模块适配器结构类图:
总结:日志模块实现采用适配器模式,日志组件(Target)、适配器以及统一接口(Log 接口) 定义清晰明确符合单一职责原则;同时,客户端在使用日志时,面向 Log 接口编程,不需要 关心底层日志模块的实现,符合依赖倒转原则;最为重要的是,如果需要加入其他第三方日 志框架,只需要扩展新的模块满足新需求,而不需要修改原有代码,这又符合了开闭原则;
日志在mybatis中的应用
BaseJdbcLogger:所有日志增强的抽象基类,用于记录 JDBC 那些方法需要增强,保存运 行期间 sql 参数信息
ConnectionLogger :打印连接信息和sql语句
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
//应用了动态代理,给真正的连接对象Connection做增强
//真正的连接对象
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 {
//如果是从Obeject继承的方法直接忽略
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
//如果是调用prepareStatement、prepareCall、createStatement的方法,打印要执行的sql语句
//并返回prepareStatement的代理对象,让prepareStatement也具备日志能力,打印参数
if ("prepareStatement".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);//打印sql语句
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);//创建代理对象
return stmt;
} else if ("prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);//打印sql语句
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);//创建代理对象
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);
}
}
}
public final class PreparedStatementLogger extends BaseJdbcLogger implements InvocationHandler {
private final PreparedStatement statement;
private PreparedStatementLogger(PreparedStatement stmt, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.statement = stmt;
}
//1,增强PreparedStatement的setxxx方法将参数设置到columnMap、columnNames、columnValues,为打印参数做好准备
//2. 增强PreparedStatement的execute相关方法,当方法执行时,通过动态代理打印参数,返回动态代理能力的resultSet
//3. 如果是查询,增强PreparedStatement的getResultSet方法,返回动态代理能力的resultSet
// 如果是更新,直接打印影响的行数
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// EXECUTE_METHODS 来自BaseJdbcLogger中初始化
if (EXECUTE_METHODS.contains(method.getName())) {//增强PreparedStatement的execute相关方法
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true);//当方法执行时,通过动态代理打印参数
}
clearColumnInfo();
if ("executeQuery".equals(method.getName())) {//返回动态代理能力的resultSet
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);
}
} else if (SET_METHODS.contains(method.getName())) {//将参数设置到columnMap、columnNames、columnValues,为打印参数做好准备
if ("setNull".equals(method.getName())) {
setColumn(params[0], null);
} else {
setColumn(params[0], params[1]);
}
return method.invoke(statement, params);
} else if ("getResultSet".equals(method.getName())) {//返回动态代理能力的resultSet
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else if ("getUpdateCount".equals(method.getName())) {// 如果是更新,直接打印影响的行数
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
public final class ResultSetLogger extends BaseJdbcLogger implements InvocationHandler {
private static Set<Integer> BLOB_TYPES = new HashSet<>();
private boolean first = true;
private int rows;
private final ResultSet rs;
private final Set<Integer> blobColumns = new HashSet<>();
static {
BLOB_TYPES.add(Types.BINARY);
BLOB_TYPES.add(Types.BLOB);
BLOB_TYPES.add(Types.CLOB);
BLOB_TYPES.add(Types.LONGNVARCHAR);
BLOB_TYPES.add(Types.LONGVARBINARY);
BLOB_TYPES.add(Types.LONGVARCHAR);
BLOB_TYPES.add(Types.NCLOB);
BLOB_TYPES.add(Types.VARBINARY);
}
private ResultSetLogger(ResultSet rs, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.rs = rs;
}
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
Object o = method.invoke(rs, params);//执行result.next方法,判断是否还有数据
if ("next".equals(method.getName())) {//如果还有数据,计数器rows加一
if (((Boolean) o)) {
rows++;
if (isTraceEnabled()) {
ResultSetMetaData rsmd = rs.getMetaData();
final int columnCount = rsmd.getColumnCount();
if (first) {
first = false;
printColumnHeaders(rsmd, columnCount);
}
printColumnValues(columnCount);
}
} else {
debug(" Total: " + rows, false);//如果没有数据了,打印rows,打印查询出来的数据条数
}
}
clearColumnInfo();
return o;
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
mybatis如何将日志加入到主体中
既然在
Mybatis
中
Executor
才是访问数据库的组件,日志功能是在
Executor
中被嵌入的
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
以上是 Mybatis源码分析之日志模块 的全部内容, 来源链接: utcz.com/z/517115.html