MyBatis代理模式

94 阅读2分钟

MyBatis 的 Service(或 Mapper 接口)能够直接通过代理模式调用,而无需手动处理 AopContext.currentProxy(),原因在于 MyBatis 和 Spring AOP 的代理机制存在以下关键差异:


1. 代理模式的实现方式不同

MyBatis 的代理
MyBatis 使用 JDK 动态代理(基于接口)为 Mapper 接口生成代理对象。当调用 sqlSession.getMapper(UserMapper.class) 时,MyBatis 会创建一个实现了 InvocationHandlerMapperProxy 对象,拦截所有接口方法调用,并将其转换为对应的 SQL 执行。
关键点:MyBatis 的代理是显式生成的(通过 getMapper 方法),且代理逻辑仅处理 SQL 映射,不涉及 Spring 的 AOP 上下文。

Spring AOP 的代理
Spring 默认使用 JDK 动态代理(基于接口)或 CGLIB(基于类)创建代理对象。若需要通过 AopContext.currentProxy() 获取当前代理,必须满足:

  1. 配置 @EnableAspectJAutoProxy(exposeProxy=true)
  2. 调用链在同一线程的 AOP 上下文中(例如,同类方法内部调用需通过代理触发)。

2. 代理对象的调用链路差异

MyBatis 的调用流程

UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 显式获取代理对象
userMapper.getUserById(1); // 直接调用代理方法

• 代理对象由 MyBatis 直接管理,调用链路清晰,无需依赖线程上下文。

Spring AOP 的调用问题
在您的 Service 类中,commonMethod() 直接调用 method() 会绕过代理(因为 this.method() 是目标对象调用,而非代理对象调用)。此时需通过 AopContext.currentProxy() 或依赖注入代理对象来触发 AOP 逻辑。


3. 设计目的的差异

MyBatis 代理
专注于将接口方法映射到 SQL 执行,不涉及复杂的 AOP 增强(如事务、日志等)。代理行为是 MyBatis 框架的核心功能,而非可选特性。

Spring AOP 代理
用于通用增强逻辑(如事务、缓存)。需要显式配置(如 exposeProxy)以支持复杂场景(如同类方法调用)。


解决方案对比

场景MyBatis 的 Mapper 接口Spring 的 Service 类
代理生成方式通过 sqlSession.getMapper() 显式生成由 Spring 容器自动生成(需配置 @EnableAspectJAutoProxy
同类调用问题无(方法调用始终通过代理)需通过 AopContext 或依赖注入代理
设计目标SQL 映射AOP 增强(事务、日志等)

总结

MyBatis 的代理能直接使用,是因为其代理对象是显式获取且功能单一(仅处理 SQL 映射),而 Spring AOP 的代理需要额外配置以支持复杂场景(如同类方法调用)。若您的 Service 需要 AOP 增强,推荐通过依赖注入代理对象(@Autowired private ServiceI selfProxy)替代 AopContext