Mybatis 与 代理模式

简介: 我们经常在工作中使用到 AOP 、在设计模式中、它属于代理模式。代理模式属于一种结构性设计模式、让你能够提供对象的替代品或占位符。代理控制着对于远对象的访问、并允许在将请求提交给对象前后进行一些处理。

网络异常,图片无法展示
|


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

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


JDK 中的动态代理


核心类就是 InvocationHandler

网络异常,图片无法展示
|


Mybatis 中代理模式


也是在日志模块中、

网络异常,图片无法展示
|

网络异常,图片无法展示
|


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

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 拦截了 createStatement 和  prepareStatement 这两个方法、分别创建对应的 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 中的 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、在对象的执行前后执行一些与对象职责无关功能
  • 延迟初始化、一些消耗资源的对象、并不需要系统启动的时候就初始化、而可以等到真正使用的时候才初始化
  • 缓存、例如连接缓存、数据缓存


优点

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


缺点

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


与其他设计模式比较

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



目录
相关文章
|
4月前
|
SQL 缓存 Java
MyBatis有哪些优缺点?
MyBatis有哪些优缺点?
27 1
|
5月前
|
设计模式 Java 数据库连接
MyBatis 设计模式解析
MyBatis 设计模式解析
|
5月前
|
SQL XML Java
Mybatis【使用注解开发】
Mybatis【使用注解开发】
|
7月前
|
SQL Java 程序员
24MyBatis - Mapper动态代理方式
24MyBatis - Mapper动态代理方式
23 0
|
10月前
|
XML SQL Java
mybatis注解方式
mybatis注解方式
|
11月前
|
XML SQL Java
【MyBatis学习笔记 五】MyBatis注解开发实现方式
【MyBatis学习笔记 五】MyBatis注解开发实现方式
85 0
|
设计模式 Java 数据库连接
Mybatis源码分析一-Mybatis基础架构以及设计模式
Mybatis源码分析一-Mybatis基础架构以及设计模式
111 0
|
SQL Java 数据库连接
Mybatis-基于代理实现CURD
基于代理实现CURD 根据ID查询 在持久层接口中添加findById方法 public User findById(Integer id); 在用户的映射配置文件中配置
38 0
|
SQL XML 缓存
Mybatis源码剖析之Mybatis执行流程(proxy 代理)
Mybatis在设计上,分为三层:接口层,数据处理层,框架支持层 在接口层中,分为 传统模式:通过sqlSession + statementId。 接口代理模式:sqlSession+mapper接口
|
SQL Java 数据库连接
Mybatis的注解开发问题
第一个:You have an error in your SQL syntax;(一定要主义格式啊) 出现这个问题,本人查了很久查不出来,
74 0