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