Mybatis Mapper接口动态代理实现原理

本文围绕MyBatis中Mapper接口无实现类却能正常调用的问题展开,介绍了MyBatis在Mapper接口上使用的动态代理非常规用法。通过Java动态代理创建代理类,说明如何将接口和XML文件中的方法关联,虽MyBatis内部实现更复杂,但此实现让接口使用更便捷。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘自《Mybatis从入门到精通》(刘增辉)

通过上面的学习,大家可能会有一个疑问,为什么 Mapper 接口没有实现类却能被正常调用呢?
这是因为 MyBaits 在 Mapper 接口上使用了动态代理的一种非常规的用法,熟悉这种动态代理的用法不仅有利于理解 MyBatis 接口和 XML 的关系,还能开阔思路 。 接下来提取出这种动态代理的主要思路,用代码来为大家说明。
假设有一个如下的 Mapper 接口。

public interface UserMapper {
	List<SysUser> selectAll();
}

这里使用 Java 动态代理方式创建一个代理类,代码如下 。

public class MyMapperProxy<T> implements InvocationHandler {
	private Class<T> mapperInterface;
	private SqlSession sqlSession;
	
	public MyMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
			this.mapperInterface = mapperInterface;
			this.sqlSession = sqlSession;
	}

	@Override
	public Object invoke(Object proxy , Method method , Object[] args)
			throws Throwable {
		//针对不同的 sql 类型,需要调用sqlSession不同的方法
		//接口方法中的参数也有很多情况 ,这里只考虑没有有参数的情况
		List<T> list= sqlSession.selectList(
				mapperInterface.getCanonicalName() +.+ method.getName());
		//返回位也有很多情况,这里不做处理直接返回
		return list;
	}
}

测试代码如下:

//获取sqlSession
SqlSession sqlSession = getSqlSession();
//获取 UserMapper 接口
MyMapperProxy userMapperProxy = new MyMapperProxy(
		UserMapper.class , sqlSession) ;
UserMapper userMapper = (UserMapper) Proxy.newProxyinstance (
		Thread.currentThread().getContextClassLoader(),
		new Class[ ] {UserMapper.class},
		userMapperProxy) ;
//调 用 selectAll 方 法
List<SysUser> user= userMapper.selectAll();

从代理类中可以看到,当调用一个接口的方法时,会先通过接口的全限定名称和当前调用的方法名的组合得到一个方法 id,这个 id 的值就是映射 XML 中 namespace 和具体方法 id 的组合。所以可以在代理方法中使用 sqlSession 以命名空间的方式调用方法。通过这种方式可以将接口和 XML 文件中的方法关联起来。这种代理方式和常规代理的不同之处在于,这里没有对某个具体类进行代理,而是通过代理转化成了对其他代码的调用。
由于方法参数和返回值存在很多种情况,因此 MyBatis 的内部实现会比上面的逻辑复杂得多,正是因为 MyBatis 对接口动态代理的实现,我们在使用接口方式的时候才会如此容易。

<think>好的,用户想了解MyBatisMapper接口实现原理,特别是动态代理和SQL映射的部分。我需要结合之前提供的引用内容来整理回答。 首先,根据引用[2]和[3],Mapper接口没有实现类MyBatis通过动态代理生成代理对象。这点是关键。用户可能疑惑为什么接口实现却能调用方法,动态代理就是答案。需要解释动态代理的作用,比如拦截方法调用。 接下来,引用[1]提到MapperProxy类,这是核心类,负责处理代理逻辑。应该详细说明MapperProxy如何拦截方法调用,将其转化为对应的SQL操作。比如,当调用selectAll()方法时,MapperProxy会找到对应的SQL语句并执行。 然后,引用[4]中的示例接口和映射文件需要关联起来。说明MyBatis如何将接口方法与XML中的SQL语句绑定,可能是通过方法名和命名空间的匹配。需要提到接口的全限定名和XML的namespace必须一致,方法名对应SQL的id。 另外,要提到SqlSession的作用,比如SqlSession.getMapper()方法触发代理对象的创建,这里可能需要结合引用[3]中的源码分析部分。 还需要注意动态代理的“非常规”用法,这里指的是不同于常见的代理方式,MyBatis没有提前编写实现类,而是在运行时动态生成。这部分可以引用引用[2]的内容。 最后,总结整个流程,从调用接口方法到代理对象处理,再到执行SQL,确保用户理解整个过程。同时,生成的相关问题需要覆盖动态代理机制、接口与XML的绑定、性能优化等可能延伸的方向。 检查是否有遗漏点,比如SQL映射的解析过程,或者配置文件的加载机制,但根据用户的问题,可能不需要太深入,保持回答在原理层面即可。</think>### MyBatis Mapper接口实现原理分步解析 #### 1. 动态代理机制 MyBatis通过JDK动态代理Mapper接口生成代理对象(核心类为$MapperProxy$)。当调用接口方法时,$MapperProxy$会拦截方法调用,并根据方法名和参数解析对应的SQL语句[^2]。 ```java // 简化的代理调用逻辑 public class MapperProxy implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) { // 1. 解析方法对应的SQL命令 // 2. 执行数据库操作 // 3. 返回处理结果 } } ``` #### 2. SQL映射绑定 接口方法与XML映射文件的绑定通过以下方式实现: - **命名空间对应**:XML的`namespace`必须与Mapper接口全限定名一致 - **方法签名匹配**:接口方法名对应XML中SQL语句的`id` - **参数映射**:通过`@Param`注解或参数顺序绑定参数 例如引用[4]中的接口方法: ```xml <!-- XML映射文件 --> <mapper namespace="com.example.UserMapper"> <select id="selectAll" resultType="User"> SELECT * FROM users </select> </mapper> ``` #### 3. 执行流程 1. 通过`SqlSession.getMapper()`获取代理对象 2. 调用接口方法时触发$MapperProxy$的`invoke()`方法 3. 通过$MapperMethod$解析SQL类型(SELECT/UPDATE等) 4. 使用$SqlSession$执行具体数据库操作 5. 结果集通过$TypeHandler$进行类型转换 #### 4. 核心组件关系 $$ \begin{array}{ccc} \text{Mapper接口} & \xrightarrow{\text{动态代理}} & \text{MapperProxy} \\ & & \downarrow \\ \text{XML映射文件} & \xleftarrow{\text{元数据解析}} & \text{Configuration} \end{array} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值