MyBatis源码分析
调用Mapper接口方法执行SQL
在第二组测试代码中,通过SqlSession.getMapper()方法获取自定义Mapper接口,并通过执行Mapper接口的方法完成sql查询,由于Mapper是接口,并没有实现类,那么它是如何完成方法执行呢?并切在与spring整合的时候又是为何Mapper接口可以直接注入?其底层原因是使用了动态代理创建Mapper的代理类进行调用的,接下来从源码进行分析其实现原理。
首先查看SqlSession.getMapper()方法,可以看到其执行链为
├─DefaultSqlSession.getMapper(Class<T> type)
└─Configuration.getMapper(Class<T> type, SqlSession sqlSession)
└─MapperRegistry.getMapper(Class<T> type, SqlSession sqlSession)
可以看到最终到了MapperRegistry类的getMapper上,MapperRegistry的作用便是对自定义Mapper接口进行管理,在MapperRegistry.getMapper()中,返回mapperProxyFactory.newInstance(sqlSession)方法执行结果。MapperProxyFactory的newInstance()方法源码如下:
可以看到这里将MapperProxy对象作为参数传给Proxy.newProxyInstance(),返回代理对象,因此我们在getMapper时获取到的Mapper对象实质是一个代理对象。而MapperProxy类实现了InvocationHandler接口,并重写了invoke方法,执行Mapper方法时实际上是通过该invoke()方法实现的,MapperProxy.invoke()源码如下:
可以看到在invoke中,如果调用构造方法则直接执行,如果调用其他方法的话这调用cachedInvoker()方法返回对象的invoke()。在cachedInvoker()中返回的是PlainMethodInvoker对象,这是一个静态内部类,其代码如下:
在PlainMethodInvoker的invoke()方法中调用的是MapperMethod的execute()方法(注:这里PlainMethodInvoker的inovke()方法和动态代理无关,是由MapperMethodInvoker接口定义的)。
由于示例代码是查询语句(),因此会走到红框位置,最终从这里可以看出,使用getMapper()来调用接口方法执行查询最终依然会使用SqlSession的selectOne()方法。
整体操作流程
整体执行流程图如下:
- 获取代理对象
- 执行Mapper接口方法
相关参考
MyBatis源码分析——MyBatis核心组件和开启SqlSession
MyBatis源码分析——使用SqlSession操作数据库
MyBatis源码分析——调用Mapper接口方法执行SQL
MyBatis源码分析——使用注解执行SQL