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

回到顶部