mybatis mapper接口实例化原理

发布时间:2022-03-01 11:25:38 作者:yexindonglai@163.com 阅读(526)

    记得每次博主去面试时,都能想象到面试官会像一个饥渴难耐的硬汉,一见到面试者都会使用加特林连环炮疯狂地发问,势必要问到盲区为止,不为别的,就为了心中那种我会你不会的优越感,为了那种心理上的快感,哪怕是自己不会的也问,当面试者回答后若有所思,甚至假想点点头在那不懂装懂;

    这不,面试题又来了:“我们都知道mybatis的mapper接口是没有实现类的,在使用的时候你知道它是如何实例化的吗?

懵逼的我:“知道啊,用的是jdk自带的动态代理;”;

饥渴的面试官:“嗯,没错,继续说,它底层做了哪些事情?”;

懵逼的我:“就是动态代理啊,还有啥?”

得意的面试官:“这样子啊,那你回去等消息吧~”

    

原理

    首先呢,mybatis的mapper接口确实是用jdk动态代理实现的,关键方法是这个newProxyInstance

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h){
  4. }

这个方法有什么作用呢?  首先它除了能作为AOP动态代理实现之外,还能用来作为mybatis的mapper接口映射,先看看这个方法是怎么使用的

  1. package com.proxy_2.proxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. /**
  6. * 生成mapper
  7. *
  8. * 面试官的问题: 既然myBatis的mapper映射是个接口,那么它是怎么实例化的呢?
  9. * 这个问题其实很好回答, 它就使用jdk自带的动态代理实现的,通过实现
  10. */
  11. public class MapperMappingTest {
  12. public static void main(String[] args) {
  13. Class<UserMapperExt> userMapperClass = UserMapperExt.class;
  14. ExtInvokeHandler handler = new ExtInvokeHandler();
  15. // 关键方法 调用一个方法后获得了
  16. UserMapperExt mapper = (UserMapperExt) Proxy.newProxyInstance(userMapperClass.getClassLoader(), new Class[]{userMapperClass}, handler);
  17. int x = mapper.addUser("1", 1);
  18. System.out.println(x);
  19. System.out.println(mapper);
  20. }
  21. }
  22. /*
  23. * -使用JDK自带动态代理实例化mapper接口,实现了InvocationHandler接口的调用处理器对象
  24. *
  25. * */
  26. class ExtInvokeHandler implements InvocationHandler {
  27. /// 只有执行mapper接口方法时才会走 invoke 方法
  28. @Override
  29. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  30. if(Object.class.equals(method.getDeclaringClass())){
  31. // 如果调用了 Object 的方法,使用当前类的方法;,因为 UserMapperExt 没有实例化,不能直接调用UserMapperExt里的方法;
  32. return method.invoke(this,args);
  33. }
  34. // 不管走mapper的哪个方法,返回值都是 1 ,入股类型不匹配则会抛出异常
  35. return 1;
  36. }
  37. }
  38. // mapper 接口
  39. interface UserMapperExt {
  40. //添加用户方法
  41. public int addUser(String name, int age);
  42. }

从上面的代码可以看到,当我们调用 Proxy.newProxyInstance() 方法时,它并没有去实例化  UserMapperExt 接口,而是将实现了 InvokeHandler 接口的ExtInvokeHandler类作为 UserMapperExt 的实例化对象;眼见为实,我们打个断点看一下

但是当我们调用 UserMapperExt.addUser() 方法的时候,因为这个方法已经在ExtInvokeHandler.invoke() 方法被拦截了,就会直接使用ExtInvokeHandler.invoke方法的返回值,而不会去走真实的 UserMapperExt.addUser()  方法;

mybatis 映射执行流程图

关键字Mybatis