结合 Mybatis 的 logging 包源码理解适配器模式、代理模式

适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
在这里插入图片描述

  • Target:目标角色,期待得到的接口.
  • Adaptee:适配者角色,被适配的接口.
  • Adapter:适配器角色,将源接口转换成目标接口.

适用场景:当调用双方都不太容易修改的时候,为了复用现有组件可以使用适配器模式;在系统中接入第三方组件的时候经常被使用到。

注意:如果系统中存在过多的适配器,会增加系统的复杂性,设计人员应考虑对系统进行重构。

logging 包里的适配器模式

MyBatis 没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的 Log 级别,且各不相同,而 MyBatis 统一提供了 trace、debug、warn、error四个级别。

自动扫描日志实现,并且第三方日志插件加载优先级如下:Slf4J → CommonsLoging → Log4J2 → Log4J → JdkLog

在这里插入图片描述
在这里插入图片描述
此图很好的表现单一职责原则、依赖倒置原则、开闭原则。

Log 接口

public interface Log {

  boolean isDebugEnabled();

  boolean isTraceEnabled();

  void error(String s, Throwable e);

  void error(String s);

  void debug(String s);

  void trace(String s);

  void warn(String s);

}

Log4jImpl

//实现Target接口
public class Log4jImpl implements Log {

	private static final String FQCN = Log4jImpl.class.getName();
	
	//这个Logger是org.apache.log4j.spi.LoggerFactory包下的类
	private final Logger log;
	
	//包含Adaptee
	public Log4jImpl(String clazz) {
	  log = Logger.getLogger(clazz);
	}
	
	@Override
	public boolean isDebugEnabled() {
	  return log.isDebugEnabled();
	}
	
	@Override
	public boolean isTraceEnabled() {
	  return log.isTraceEnabled();
	}
	
	@Override
	public void error(String s, Throwable e) {
	  log.log(FQCN, Level.ERROR, s, e);
	}
	
	@Override
	public void error(String s) {
	  log.log(FQCN, Level.ERROR, s, null);
	}
	
	@Override
	public void debug(String s) {
	  log.log(FQCN, Level.DEBUG, s, null);
	}
	
	@Override
	public void trace(String s) {
	  log.log(FQCN, Level.TRACE, s, null);
	}
	
	@Override
	public void warn(String s) {
	  log.log(FQCN, Level.WARN, s, null);
	}
}

以 Log4jImpl 为例,此类作为 Adapter 实现了 Log 接口,里面又持有了一个 log4j 的 Logger 对象。
在实现接口方法的时候,只需要调用 Logger 对象对应的方法就行了。

如此一来,可以将各种第三方日志的不同日志级别,转换成自己的日志级别。是不是很妙呢?

那么,Mybatis 是如何实现按自己定义的优先级加载第三方日志的呢?
这需要阅读下面这个类

LogFactory 实现自动加载

public final class LogFactory {

  /**
   * Marker to be used by logging implementations that support markers
   */
  public static final String MARKER = "MYBATIS";

  //被选定的第三方日志组件适配器的构造方法
  private static Constructor<? extends Log> logConstructor;

  //自动扫描日志实现,并且第三方日志插件加载优先级如下:Slf4J → CommonsLoging → Log4J2 → Log4J → JdkLog
  static {
  	//如果看不懂::下面这三条等价
    //Runnable runnable = LogFactory::useSlf4jLogging;
    //Runnable runnable = () -> LogFactory.useSlf4jLogging();
    //Runnable runnable = new Runnable() {
    //      @Override
    //      public void run() {
    //        LogFactory.useSlf4jLogging();
    //      }
    //    };
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

  private LogFactory() {
    // disable construction
  }

  public static Log getLog(Class<?> aClass) {
    return getLog(aClass.getName());
  }

  public static Log getLog(String logger) {
    try {
      return logConstructor.newInstance(logger);
    } catch (Throwable t) {
      throw new LogException("Error creating logger for logger " + logger + ".  Cause: " + t, t);
    }
  }

  public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
    setImplementation(clazz);
  }

  public static synchronized void useSlf4jLogging() {
    setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
  }

  public static synchronized void useCommonsLogging() {
    setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class);
  }

  public static synchronized void useLog4JLogging() {
    setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class);
  }

  public static synchronized void useLog4J2Logging() {
    setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class);
  }

  public static synchronized void useJdkLogging() {
    setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class);
  }

  public static synchronized void useStdOutLogging() {
    setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class);
  }

  public static synchronized void useNoLogging() {
    setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class);
  }


  private static void tryImplementation(Runnable runnable) {
    //如果没有构造方法
    if (logConstructor == null) {
      try {
      	//LogFactory.useSlf4jLogging();
      	//setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class);
        runnable.run();
      } catch (Throwable t) {
        // ignore
      }
    }
  }
  //通过指定的log类来初始化构造方法
  private static void setImplementation(Class<? extends Log> implClass) {
    try {
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      Log log = candidate.newInstance(LogFactory.class.getName());
      if (log.isDebugEnabled()) {
        log.debug("Logging initialized using '" + implClass + "' adapter.");
      }
      logConstructor = candidate;
    } catch (Throwable t) {
      throw new LogException("Error setting Log implementation.  Cause: " + t, t);
    }
  }
}

代理模式

在这里插入图片描述

与适配器模式的区别是:

  • 适配器模式中,Adapter 和 Adaptee 有不同的访问方式
  • 代理模式中,Proxy 和 RealSubject 有相同的访问方式

所以,适配器模式用于转化访问方式,而代理模式用于增强。

logging 包里的代理模式

在这里插入图片描述
在这里插入图片描述

ConnectionLogger

对 Connection 的增强

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 {
    	//如果是从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);
    }
  }

  /*
   * Creates a logging version of a connection
   *
   * @param conn - the original connection
   * @return - the connection with logging
   */
  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);
  }

  /*
   * return the wrapped connection
   *
   * @return the connection
   */
  public Connection getConnection() {
    return connection;
  }
}

PreparedStatementLogger

对 PreparedStatement 的增强

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);
      }
      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);
    }
  }

  /*
   * Creates a logging version of a PreparedStatement
   *
   * @param stmt - the statement
   * @param sql  - the sql statement
   * @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);
  }

  /*
   * Return the wrapped prepared statement
   *
   * @return the PreparedStatement
   */
  public PreparedStatement getPreparedStatement() {
    return statement;
  }

}

ResultSetLogger

对 ResultSet 的增强

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);
    }
  }

  private void printColumnHeaders(ResultSetMetaData rsmd, int columnCount) throws SQLException {
    StringBuilder row = new StringBuilder();
    row.append("   Columns: ");
    for (int i = 1; i <= columnCount; i++) {
      if (BLOB_TYPES.contains(rsmd.getColumnType(i))) {
        blobColumns.add(i);
      }
      String colname = rsmd.getColumnLabel(i);
      row.append(colname);
      if (i != columnCount) {
        row.append(", ");
      }
    }
    trace(row.toString(), false);
  }

  private void printColumnValues(int columnCount) {
    StringBuilder row = new StringBuilder();
    row.append("       Row: ");
    for (int i = 1; i <= columnCount; i++) {
      String colname;
      try {
        if (blobColumns.contains(i)) {
          colname = "<<BLOB>>";
        } else {
          colname = rs.getString(i);
        }
      } catch (SQLException e) {
        // generally can't call getString() on a BLOB column
        colname = "<<Cannot Display>>";
      }
      row.append(colname);
      if (i != columnCount) {
        row.append(", ");
      }
    }
    trace(row.toString(), false);
  }

  /*
   * Creates a logging version of a ResultSet
   *
   * @param rs - the ResultSet to proxy
   * @return - the ResultSet with logging
   */
  public static ResultSet newInstance(ResultSet rs, Log statementLog, int queryStack) {
    InvocationHandler handler = new ResultSetLogger(rs, statementLog, queryStack);
    ClassLoader cl = ResultSet.class.getClassLoader();
    return (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, handler);
  }

  /*
   * Get the wrapped result set
   *
   * @return the resultSet
   */
  public ResultSet getRs() {
    return rs;
  }
}

来看看调用流程

类 SimpleExecutor

//创建Statement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  //获取connection对象的动态代理,添加日志能力;
  Connection connection = getConnection(statementLog);
  //通过不同的StatementHandler,利用connection创建(prepare)Statement
  stmt = handler.prepare(connection, transaction.getTimeout());
  //使用parameterHandler处理占位符
  handler.parameterize(stmt);
  return stmt;
}

跟 getConnection:
类 BaseExecutor,SimpleExecutor 的父类

protected Connection getConnection(Log statementLog) throws SQLException {
    Connection connection = transaction.getConnection();
    if (statementLog.isDebugEnabled()) {
    						//前面看到过的方法,返回代理对象
      return ConnectionLogger.newInstance(connection, statementLog, queryStack);
    } else {
      return connection;
    }
  }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值