mybatis的DAO接口为什么不需要实现类?深入理解mybatis实现原理

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

博客主页:JavaProfessional
一个专注于Java的博主,致力于使用通俗易懂的语句讲解内容,期待您的关注!

你将获得什么?

  1. 为什么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是如何实例化代理对象的吧。

代理类初始化

  1. 新建MapperProxy对象

在这里插入图片描述

  1. 设置代理
    在这里插入图片描述

这个代理方式说明在执行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方法时走DefaultMethodInvokerinvoke方法,否则走PlainMethodInvokerinvoke方法

getUserById不是default方法,所以我们走PlainMethodInvokerinvoke方法,如下图:
在这里插入图片描述

终于见到excute方法了/(ㄒoㄒ)/~~
点进入excute方法,我们看到如下程序:
在这里插入图片描述

这就是mybatis在执行方法前为我们代理执行的方法。SELECTUPDATEDELETEINSERT都会分别执行不同的程序。这就是为什么mybatis接口可以不用实现类,因为代理程序都为我们做了。

总结

跳来跳去的是不是有点晕了,感觉还是记不住?我们最后使用一张图来总结一下:
在这里插入图片描述

好文推荐

  1. 通俗易懂JVM
  2. 为什么Spring中的IOC(控制反转)能够降低耦合性(解耦)?
  3. 效率翻倍,这些idea的逆天功能你知道吗?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tinpo_123

感谢给小张填杯java~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值