Mybatis 与 代理模式

我们经常在工作中使用到 AOP 、在设计模式中、它属于代理模式。

代理模式属于一种结构性设计模式、让你能够提供对象的替代品或占位符。代理控制着对于远对象的访问、并允许在将请求提交给对象前后进行一些处理。

Mybatis 与 代理模式_第1张图片

JDK 中的动态代理

核心类就是 InvocationHandler

Mybatis 与 代理模式_第2张图片

Mybatis 中代理模式

也是在日志模块中、

Mybatis 与 代理模式_第3张图片

Mybatis 与 代理模式_第4张图片

这些代理类从其名字中可以猜测出其用来打印日志

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 {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, params);
      }
      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);
        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 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);
  }

  public Connection getConnection() {
    return connection;
  }

}

ConnectionLogger 拦截了 createStatementprepareStatement 这两个方法、分别创建对应的 Logger 代理。在 debug 开启的情况下还打印了相关信息

PreparedStatementLogger 的 invoke 方法、这个代理方法主要还是打印相关参数信息

@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())) {
      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 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);
  }
}

Mybatis 创建 Connection 代理

BaseExecutor 中如果开启了 debug 模式、则会创建 Connection 代理

Mybatis 与 代理模式_第5张图片

连接池中的代理模式

Mybatis 中的 PooledConnection 也是使用了代理模式

class PooledConnection implements InvocationHandler {
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    if (CLOSE.equals(methodName)) {
     	// 将连接归还到连接池
      dataSource.pushConnection(this);
      return null;
    }
    try {
      if (!Object.class.equals(method.getDeclaringClass())) {
        checkConnection();
      }
      return method.invoke(realConnection, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
}  

主要做的功能是关闭连接到时候、代理该方法、不是直接关闭连接、而是归还到池中、等待下一次使用。

使用场景

  • AOP、在对象的执行前后执行一些与对象职责无关功能
  • 延迟初始化、一些消耗资源的对象、并不需要系统启动的时候就初始化、而可以等到真正使用的时候才初始化
  • 缓存、例如连接缓存、数据缓存

优点

  • 客户端无感的情况下控制服务对象
  • 开闭原则、可以在不对服务或者客户端作出修改的情况下创建新代理。

缺点

  • 代码变得复杂、中间多了代理类这一层
  • 服务响应可能会变慢

与其他设计模式比较

  • 适配器模式能为被封装对象提供不同的接口、代理模式能为对象提供相同的接口、装饰模式则能为对象提供加强接口。
  • 装饰和代理有着相似的结构、但是其意图却非常不同、这两个模式都是基于组合原则、也就是说一个对象应该将部分工作委派给另一个对象。代理模式通常管理其服务对象的生命周期(比如说连接缓存、是归还池中复用、还是真的关闭。)、而装饰模式的生成则是由客户端自行控制组合的。

你可能感兴趣的:(Mybatis,代理模式,java,开发语言)