mybatis mapper接口实例化原理

发布时间 2023-08-04 13:21:27作者: porter_代码工作者

 

 

 

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

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

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

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

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

 

原理

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

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

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

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

 

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

 

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

 

 

mybatis 映射执行流程图

 

转载:https://blog.csdn.net/qq_27184497/article/details/117398441