MyBatis Mapper 接口动态代理与 Spring 注入原理
·
MyBatis Mapper 接口动态代理与 Spring 注入原理
1. Mapper 接口的本质
public interface UserMapper {
User findById(Long id);
}
- 接口没有实现类,只有方法定义。
- MyBatis 通过 XML 或注解映射 SQL。
- Spring 容器要注入 Mapper,必须生成对象实例。
2. Spring + MyBatis 如何生成 Mapper Bean
2.1 扫描 Mapper 接口
@MapperScan("com.elias.pay.mapper")
- Spring + MyBatis 的
MapperScannerConfigurer会扫描指定包下的接口。 - 找到所有 Mapper 接口,例如
UserMapper, OrderMapper。
2.2 为接口生成代理对象
- JDK 动态代理(默认方式):proxy 就是一个 接口的实现对象
UserMapper proxy = (UserMapper) Proxy.newProxyInstance(
interfaceClass.getClassLoader(),
new Class[]{UserMapper.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 调用 SqlSession 执行 SQL
return sqlSession.selectOne(method.getName(), args);
}
}
);
-
说明:
Proxy.newProxyInstance生成接口代理对象invoke()拦截方法调用,执行对应 SQL- 返回的
proxy是 Spring 容器中注入的 Mapper Bean
-
CGLIB 代理:
- 通过继承类生成代理(可选)
- 原理类似:拦截方法调用 → 执行 SQL
3. Spring 注入流程
- 扫描 Mapper 接口 → 找到所有接口
- 生成 MapperFactoryBean → 注册到 Spring 容器
- 创建动态代理对象 → 实现接口、拦截方法调用
- 注入到容器 → 使用
@Autowired注入 Bean - 调用 Mapper 方法 → 代理拦截 → 执行 SQL → 返回结果
@Autowired
private UserMapper userMapper;
userMapper.findById(1L); // 实际执行代理对象的 invoke() → 执行 SQL
4. 内部调用与事务代理说明
- 代理对象的方法调用 → 触发切面逻辑(事务、日志等)
- 同类内部方法调用
this.method()→ 绕过代理对象 → 不触发事务 - 外部调用 Bean 方法 → 通过代理对象 → 事务生效
| 调用方式 | 事务是否生效 | 原因 |
|---|---|---|
内部调用 this.method() |
不生效 | 绕过代理对象,事务拦截器没触发 |
| 外部调用 Bean 方法 | 生效 | 调用代理对象 → 事务拦截器触发 |
5. 核心总结
- Mapper 接口本身没有实现类,Spring + MyBatis 通过动态代理生成对象。
- 代理对象负责拦截方法调用,并执行对应 SQL。
- 注入容器的是 代理对象,调用方法时会执行 SQL。
- 内部调用不会触发事务 / 切面逻辑,外部调用代理对象方法才生效。
更多推荐




所有评论(0)