2.Mybatis源码分析:动态代理实现JDBC打印日志

Mybatis使用了动态代理来打印JDBC的执行日志。本文默认已经对Java动态代理有所了解。

在这里插入图片描述

Mybatis首先对所有java.sql.Connection进行代理,打印日志。返回代理对象,接下来sql参数的日志由PreparedStatement和Statement的代理对象完成。
ConnectionLogger:

 if ("prepareStatement".equals(method.getName())) {
        // 当调用prepareStatement,返回带日志功能的代理对象
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }
        //在这里返回PreparedStatement的代理对象
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("prepareCall".equals(method.getName())) {
        // 调用prepareCall,返回带日志功能的代理对象
        if (isDebugEnabled()) {
          debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
        }
        PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
        stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      } else if ("createStatement".equals(method.getName())) {
        // 调用createStatement,返回带日志Statement代理对象
        Statement stmt = (Statement) method.invoke(connection, params);
        stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
        return stmt;
      }

接下来是PreparedStatementLogger,这个类是对PreparedStatement生成带日志的代理。

package org.apache.ibatis.logging.jdbc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import org.apache.ibatis.logging.Log;
import org.apache.ibatis.reflection.ExceptionUtil;

/**
 * PreparedStatement动态代理,用于增加日志.
 *
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
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;
	}

	/**
	 * 创建有日志版本的PreparedStatement
	 *
	 * @param stmt         - the statement
	 * @param statementLog - the statement log
	 * @param queryStack   - the query stack
	 * @return - the proxy
	 */
	public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLog, int queryStack) {
		InvocationHandler handler = new PreparedStatementLogger(stmt, statementLog, queryStack);
		ClassLoader cl = PreparedStatement.class.getClassLoader();
		return (PreparedStatement) Proxy.newProxyInstance(cl,
				new Class[]{PreparedStatement.class, CallableStatement.class}, handler);
	}

    /** 在设置sql参数时把参数保存下来, 在执行之前打印sql参数日志, 并且结果集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执行方法的静态常量,包括{"execute", "executeUpdate","executeQuery","addBatch"}
			if (EXECUTE_METHODS.contains(method.getName())) {
				//当调用执行方法时,打印参数日志,如果有返回结果,那么对ResultSet进行日志代理
				if (isDebugEnabled()) {
					debug("Parameters: " + getParameterValueString(), true);
				}
				clearColumnInfo();
				if ("executeQuery".equals(method.getName())) {
					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())) {
				//调用设置参数的方式,将参数记录好,以供后续打印参数的日志
				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())) {
				//调用getUpdateCount自动打印日志
				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);
		}
	}

	/**
	 * 返回包装的statement.
	 *
	 * @return the PreparedStatement
	 */
	public PreparedStatement getPreparedStatement() {
		return statement;
	}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值