CoderLi
V1
2022/03/26阅读:77主题:前端之巅同款
Mybatis 与 代理模式
我们经常在工作中使用到 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、在对象的执行前后执行一些与对象职责无关功能 -
延迟初始化、一些消耗资源的对象、并不需要系统启动的时候就初始化、而可以等到真正使用的时候才初始化 -
缓存、例如连接缓存、数据缓存
优点
-
客户端无感的情况下控制服务对象 -
开闭原则、可以在不对服务或者客户端作出修改的情况下创建新代理。
缺点
-
代码变得复杂、中间多了代理类这一层 -
服务响应可能会变慢
与其他设计模式比较
-
适配器模式能为被封装对象提供不同的接口、代理模式能为对象提供相同的接口、装饰模式则能为对象提供加强接口。 -
装饰和代理有着相似的结构、但是其意图却非常不同、这两个模式都是基于组合原则、也就是说一个对象应该将部分工作委派给另一个对象。代理模式通常管理其服务对象的生命周期(比如说连接缓存、是归还池中复用、还是真的关闭。)、而装饰模式的生成则是由客户端自行控制组合的。
作者介绍
CoderLi
V1