三个类告诉你MyBatis是如何用动态代理实现的

Posted 浮生(FS)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三个类告诉你MyBatis是如何用动态代理实现的相关的知识,希望对你有一定的参考价值。

今天来讲一下mybatis的实现原理,我们都知道mybatis是使用动态代理的方式实现的一套ORM框架,那么他是怎么实现的呢?

在回答这个问题之前,我们先来捋一下mybatis框架的使用流程,一般我们会把他跟spring一起结合使用,spring容器来统一管理所有对象,使用mybatis时首先要进行几个配置。

  1. dao的包路径(也就是接口对象的包路径)
  2. xml文件的路径

我们都知道使用mybatis开发的时候,一般是一个接口类对应一个xml文件,接口中的每个方法也对应着xml中的document节点,这里我就不详细说,用过的都知道,那么mybatis就是通过动态代理的方式根据我们的dao层的接口以及接口方法对应的xml去自动生成的实现类,统一帮我们做掉了建立数据连接,拼接sql语句,执行sql语句,转换sql返回值到对象等这一些列的动作。

下面说一下简单实现他的工作流程:

  1. 项目启动时根据xml文件路径读取所有的xml信息,到map中存储,map的key可以定义为类名+方法名
  2. 编写代理类,代理类负责根据被代理的类名+方法名,读取对应的sql配置,然后根据入参,拼接解析出完整sql,然后交给jdbc去执行,最后将返回数据转换成接口返回值的对象做返回
  3. 根据dao的包路径读取所有的需要代理的dao对象,利用上面第二条写的代理类来循环为每个dao创建代理类
  4. 将所有生成的dao代理类放到spring容器中进行管理,使用时直接通过spring的注解就可以注入被代理过的dao

ok下面看下简单的代码(PS:前方高能,代码过于简陋😂,不然也不会只有3个类,大家懂意思就可以了)

// 先定义一个dao接口,有两个方法
interface ITestDao 

    String test1(@Param("code") String code);

    String test2(@Param("code") String code);

// 创建一个代理类,实现InvocationHandler接口,这里xml就不建文件了,直接写死在这里了大家懂意思就好拉,哈哈哈。。。
public class MybatisProxy implements InvocationHandler 

    public static Map<String,String> xmlMap = new HashMap<String,String>();

    static
        // 系统初始化读取xml放入map
        // 假装这里是从xml重读取的sql数据,当然实际mybatis读取的数据结构更复杂一些,比如入参类型出参类型等,这里暂且不表
        xmlMap.put("ITestDao.test1","select * from test1 where code=:code");
        xmlMap.put("ITestDao.test2","select * from test2 where code=:code");
    

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
        // 类名加方法名作为读取该方法的sql的key
        String name = method.getDeclaringClass().getName() + "." + method.getName();
        // 从map中读取
        String sql = MybatisProxy.xmlMap.get(name);
        // 判断方法参数读取参数拼接到sql上
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < parameterAnnotations.length; i++)
            for (Annotation annotation:parameterAnnotations[i]) 
                if(annotation instanceof Param)
                    String value = ((Param) annotation).value();
                    sql = sql.replace(":"+value,"'" + args[i] + "'");
                
            

        
        // 计算出来最终要执行的sql
        System.out.println("do sql : " + sql);
        return jdbc(sql,method);
    

    private Object jdbc(String sql,Method method) 
        // 1. 通过jdbc连接数据库,然后执行sql
        // 2. 把返回结果根据方法的返回值类型做转换,然后返回
        return null;
    

// 最后一个就是系统启动的时候用来生成实现类并且放到spring中的,下面这个只是一个main方法用来测试看效果的,实际参考最开始的那个流程,以及代码里面的注释
public class CreateProxyObj 

    // dao所有在的包路径
    public static String DAO_PACKAGE = "com.xxx.xxx.dao";

    public static <T> T createProxyObj(Class<T> clazz)
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] clazz,new MybatisProxy());
    

    // 当然不是用main方法跑的,需要系统初始化时自动执行
    public static void main(String[] args)
        // 1. 根据DAO_PACKAGE包找到这个包下所有的class
        // 2. 循环给每个class创建代理,创建方式如下
        ITestDao proxyObj = CreateProxyObj.createProxyObj(ITestDao.class);

        // 创建完了其实就可以用了
        proxyObj.test1("111");
        proxyObj.test2("222");
        // 但通常创建完了会把代理过的对象,放到spring容器中,这样使用的时候就能够通过@Autowired注解的方式注入使用拉
    

以上就是,mybatis的一个简单的实现原理,也清晰的向大家展示了动态代理的实现使用方式用法。

以上是关于三个类告诉你MyBatis是如何用动态代理实现的的主要内容,如果未能解决你的问题,请参考以下文章

MyBatis是如何为Dao接口创建实现类的

动态代理之投鞭断流!看一下MyBatis的底层实现原理!

如何用Java动态代理实现AOP

mybatis原理

使用动态代理实现dao接口

Mybatis之jdk动态代理和cglib动态代理