博客主页:JavaProfessional
一个专注于Java的博主,致力于使用通俗易懂的语句讲解内容,期待您的关注!
你将获得什么?
- 为什么mybatis的dao层不需要接口?
编写一个mybatis项目
请参照博主的另一篇博客
探究源码之前
在探究源码之前,我们想一想自己的interface可以没有实现类吗?我们先来尝试一下:
新增一个接口,这个接口就是我们不要实现类却能执行的接口
package com.pai;
public interface Mapper {
Integer count();
}
新增动态代理类:
package com.pai;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MapperProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return this.readSQL(method.getName());
}
public Integer readSQL(String methodName) {
return 1;
}
}
新增主方法,生成动态代理对象:
package com.pai;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) {
// 生成动态代理对象
Mapper mapper = (Mapper) Proxy.newProxyInstance(
Mapper.class.getClassLoader(),
new Class[]{Mapper.class}, new MapperProxy());
Integer count = mapper.count();
System.out.println(count);
}
}

我们也生产了一个不用实现类的接口!
逐步调试
查看主程序

我们发现在第26行是获取mapper,我们首先在此打断点。
获取mapper
我们顺着getMapper到达MapperRegistry.getMapper(Class<T> type, SqlSession sqlSession)方法,代码如下:

这里从knownMappers中获取mapper的代理工厂(这个在项目初始化的时候就被加入进去了)
我们进入newInstance方法,查看MapperProxyFactory是如何实例化代理对象的吧。
代理类初始化
- 新建MapperProxy对象

- 设置代理

这个代理方式说明在执行mapperInterface这个接口的任何方法之前,都会执行代理mapperProxy对象的invoke方法。我们点入MapperProxy代理类,发现确实实现了动态代理的InvocationHandler并且实现了invoke方法。

具体到本程序,就是在执行UserDao接口的getUserById方法前,会执行invoke方法。
代理方法
我们详细来看一看在执行getUserById之前都会做哪些事情吧~

我们发现它会将被代理对象的类和Object类比较一下,为什么要这样呢?
因为动态代理是被代理类UserDao的所有方法都会走invoke方法,那么UserDao祖先类是Object,那么Object类的方法也会走,比如equals方法,这些方法我们用去执行mybatis的逻辑吗?肯定是不用的呀,所以要判断一下。
getUserById方法肯定不是Object类的方法,所以我们走三元表达式的后半部分:
this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
cachedInvoker是获取缓存的执行器,这部分不属于我们今天的重点,我们在mybatis的缓存中再说明,直接看invoke方法.
执行

我们发现其有两个实现类,那么这两个实现类有什么区别呢?
当执行的方法是interface里面的default方法时走DefaultMethodInvoker的invoke方法,否则走PlainMethodInvoker的invoke方法
getUserById不是default方法,所以我们走PlainMethodInvoker的invoke方法,如下图:

终于见到excute方法了/(ㄒoㄒ)/~~
点进入excute方法,我们看到如下程序:

这就是mybatis在执行方法前为我们代理执行的方法。SELECT、UPDATE、DELETE、INSERT都会分别执行不同的程序。这就是为什么mybatis接口可以不用实现类,因为代理程序都为我们做了。
总结
跳来跳去的是不是有点晕了,感觉还是记不住?我们最后使用一张图来总结一下:

本文通过实例代码解析了MyBatis如何实现接口无需实现类即可执行的方法。通过创建动态代理类MapperProxy,利用Java反射机制,在执行Mapper接口方法前调用代理类的invoke方法,完成SQL的执行。在invoke方法中,根据方法类型选择不同的执行器执行SQL,展示了MyBatis的动态代理在简化代码的同时,实现数据库操作的核心过程。

12万+

被折叠的 条评论
为什么被折叠?



