Mybatis——2.2 SQL的映射
Mybatis——2.2 SQL的映射
- 1.代理模式
- 2.如何执行接口方法
- 示例demo
- 3.Mybatis是如何做的
- 3.1猜想
- 3.2源码探究
“Java中非静态方法的运行需要实例对象才能运行(即对象点方法),Mybatis中的Mapper都是接口,也没有实现类,那么接口中的方法怎么就被调用执行了呢?”
这是当时用Mybatis时最困扰我的一个问题,搜的资料博客中,大多来一句“动态代理”一笔带过。留我一人风中凌乱,难道"动态代理"四个字这么形象生动、易于理解吗…
也因为这个疑问,了解了代理模式,动态代理,以及Mybatis如何使用动态代理完成mapper接口方法的调用。
1.代理模式
这里简单说一下,例子就不举了,网上都是。说一下工作中的使用场景,一般是某个类无法修改或不敢改动,那就在目标类外面包一层代理类,即不用修改目标类,又可以对目标类做功能的增强。其原理就是:通过实现和目标类相同的接口来平替目标类,然后通过构造函数获取目标类的对象引用,增强的功能由代理类完成,核心的逻辑依旧通过目标类进行调用。(我记得好像写过对应的设计模式笔记,感兴趣的可以翻一下)
2.如何执行接口方法
前面提到的代理模式和文章开头的问题有关系吗?当然有关系,如果你理解了什么是代理,文章开头的问题就可以通过代理来实现。
即使接口没有实现类
,也可以通过InvocationHandler
为其创建代理类,通过代理类执行接口方法。这是java反射包下的一个接口,可以为接口生成代理对象。简单代码示例:
示例demo
// 定义接口
public interface Interface01 {String m1();int m2();
}
// 定义调用处理器
public class Interface01Handler implements InvocationHandler {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if(method.getName().equals("m1")) {return "hello world";}else {return 1024;}}
}
// 创建代理对象测试
public static void main(String[] args) {// 代理对象Object proxy = Proxy.newProxyInstance(Interface01.class.getClassLoader(),new Class[]{Interface01.class}, new Interface01Handler());if(proxy instanceof Interface01) {// 调用接口方法Interface01 a = (Interface01) proxy;System.out.println(a.m1());System.out.println(a.m2());}
}
上述代码中,接口并没有实现类,但接口方法可以被调用。当然你也可以理解成:接口是以另一种形式被实现,接口方法以另一种形式被定义。
3.Mybatis是如何做的
通过反射可以为接口创建代理对象。知道了Java中的这个机制后,回到Mybatis的疑问,mapper也是接口,也没有实现类但最终其方法可以被调用,类比一下,你应该可以猜到Mybatis是如何来实现的。
3.1猜想
我们猜想Mybatis是使用了Java的反射和代理,方法执行是由InvocationHandler实现类的invoke()方法来完成。有以下几点疑问:
- 在demo中,接口的方法逻辑是在InvocationHandler的实现类中写死的,即 invoke的逻辑。如果Mybatis是按照示例代码的逻辑去实现的,调用处理器的逻辑在什么地方?Mybatis中我们没有编写调用处理器的逻辑。
- 回想一下InvocationHandler的invoke()方法,它其实就是执行接口中某个方法的逻辑。而mapper.xml中定义的SQL片段恰恰就是方法的执行逻辑。
- 于是我们猜想,Mybatis确实是使用示例代码相同的模式来创建代理对象,但代理对象的调用处理器的执行逻辑则是使用mapper.xml文件来完成。
那么mapper.xml中的方法如何与代理对象中的invoke()方法进行映射和绑定呢?当我们调用mapper接口的某个方法时,调用处理器如何自动执行mapper.xml中同名的SQL呢?这一步如果通了,Mybatis中SQL映射的核心也就知道的差不多了
3.2源码探究
(未完待)