• 【手写Mybatis】step01:创建简单的代理工厂


    一、设计

    思考:用户自定义的数据库接口,是怎么和配置的xml文件,数据库三者联合起来的,最简单的方法就是代理模式,生成代理对象执行数据库操作。设计图如下:
    在这里插入图片描述
    IUserDao:用户自定义的数据库接口
    MapperProxyFactory:获取代理对象
    MapperProxy:代理对象进行数据库的操作

    二、实现

    在这里插入图片描述
    目前这个 Mybatis 框架的代理操作实现的还只是最核心的功能,相当于是光屁股的娃娃,还没有添加衣服。不过这样渐进式的实现可以让大家先了解到最核心的内容,后续我们在陆续的完善。

    MapperProxy 负责实现 InvocationHandler 接口的 invoke 方法,最终所有的实际调用都会调用到这个方法包装的逻辑。

    MapperProxyFactory 是对 MapperProxy 的包装,对外提供实例化对象的操作。当我们后面开始给每个操作数据库的接口映射器注册代理的时候,就需要使用到这个工厂类了。

    2.1代码结构

    
    step-01-simplefactory
    └── src
        ├── main
        │   └── java
        │       └── com.qf.mybatis.bind
        │           ├── MapperProxy.java
        │           └── MapperProxyFactory.java
        └── test
            └── java
                └── com.qf.mybatis
                    ├── dao
                    │   └── IUserDao.java
                    └── ApiTest.java
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.2映射器类

    package com.qf.mybatis.bind;
    
    import java.io.Serializable;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.Map;
    
    /**
     * jdk动态代理生成代理对象,代理对象执行对应的方法
     * @param 
     */
    public class MapperProxy<T> implements InvocationHandler, Serializable {
    
        private static final long serialVersionUID = -6424540398559729838L;
    
        private Map<String,String> sqlSession;
        private final Class<T> mapperInterface;
    
        public MapperProxy(Map<String, String> sqlSession, Class<T> mapperInterface) {
            this.sqlSession = sqlSession;
            this.mapperInterface = mapperInterface;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //执行代理对象的方法
            if (proxy.getClass().equals(method.getDeclaringClass())){
                return method.invoke(proxy,args);
            }else{
                return "你的被代理了"+sqlSession.get(mapperInterface.getName()+"."+method.getName());
            }
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    通过实现 InvocationHandler#invoke 代理类接口,封装操作逻辑的方式,对外接口提供数据库操作对象。

    目前我们这里只是简单的封装了一个 sqlSession 的 Map 对象,你可以想象成所有的数据库语句操作,都是通过接口名称+方法名称作为key,操作作为逻辑的方式进行使用的。那么在反射调用中则获取对应的操作直接执行并返回结果即可。当然这还只是最核心的简化流程,后续不断补充内容后,会看到对数据库的操作

    另外这里要注意如果是 Object 提供的 toString、hashCode 等方法是不需要代理执行的,所以添加 Object.class.equals(method.getDeclaringClass()) 判断

    2.3代理工厂类

    package com.qf.mybatis.bind;
    
    import java.lang.reflect.Proxy;
    import java.util.Map;
    
    public class MapperProxyFactory<T>{
    
        private final Class<T> mapperInterface;
    
        public MapperProxyFactory(Class<T> mapperInterface) {
            this.mapperInterface = mapperInterface;
        }
    
        public T newInstance(Map<String,String> sqlSession){
            final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface);
            return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    使用jdk动态代理,生成代理对象

    3.、测试

    用户自定义的数据库接口

    package com.qf.mybatis.dao;
    
    public interface UserDao {
        String queryName(String uid);
        String queryUser(String uid);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ApiTest

    package com.qf.mybatis.dao;
    
    
    import com.qf.mybatis.bind.MapperProxy;
    import com.qf.mybatis.bind.MapperProxyFactory;
    import org.junit.Test;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class ApiTest {
        private Logger logger=LoggerFactory.getLogger(ApiTest.class);
    
    
        @Test
        public void Test(){
            Map<String,String> sqlSession=new HashMap<String, String>();
            sqlSession.put("com.qf.mybatis.dao.UserDao.queryName","查询用户名字");
            sqlSession.put("com.qf.mybatis.dao.UserDao.queryUser","查询用户信息");
    
            MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(UserDao.class);
            UserDao userDao = (UserDao) mapperProxyFactory.newInstance(sqlSession);
            String queryName = userDao.queryName("111L");
            logger.info("结果:{}",queryName);
            String queryUser = userDao.queryUser("111L");
            logger.info("结果:{}",queryUser);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    结果:

    "C:\Program Files\Java\jdk1.8.0_301\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\idea2020\IntelliJ IDEA 2020.2.4\lib\idea_rt.jar=17148:D:\idea2020\IntelliJ IDEA 2020.2.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\idea2020\IntelliJ IDEA 2020.2.4\lib\idea_rt.jar;D:\idea2020\IntelliJ IDEA 2020.2.4\plugins\junit\lib\junit5-rt.jar;D:\idea2020\IntelliJ IDEA 2020.2.4\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_301\jre\lib\rt.jar;D:\zhoutao\handwrite-mybatis\step-01-simplefactory\target\test-classes;D:\zhoutao\handwrite-mybatis\step-01-simplefactory\target\classes;C:\Users\Administrator\.m2\repository\com\alibaba\fastjson\1.2.75\fastjson-1.2.75.jar;C:\Users\Administrator\.m2\repository\org\dom4j\dom4j\2.1.3\dom4j-2.1.3.jar;C:\Users\Administrator\.m2\repository\junit\junit\4.7\junit-4.7.jar;C:\Users\Administrator\.m2\repository\cn\hutool\hutool-all\5.5.0\hutool-all-5.5.0.jar;C:\Users\Administrator\.m2\repository\org\slf4j\slf4j-api\1.7.5\slf4j-api-1.7.5.jar;C:\Users\Administrator\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.5\jcl-over-slf4j-1.7.5.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-classic\1.0.9\logback-classic-1.0.9.jar;C:\Users\Administrator\.m2\repository\ch\qos\logback\logback-core\1.0.9\logback-core-1.0.9.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.qf.mybatis.dao.ApiTest,Test
    16:11:09.013 [main] INFO  com.qf.mybatis.dao.ApiTest - 结果:你的被代理了查询用户名字
    16:11:09.016 [main] INFO  com.qf.mybatis.dao.ApiTest - 结果:你的被代理了查询用户信息
    
    Process finished with exit code 0
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从结果来看,我们自定义的接口已经被代理成功了,代理对象替我们做后续数据库操作

    4、总结

    本章节我们初步对 Mybatis 框架中的数据库 DAO 操作接口和映射器通过代理类的方式进行链接,这一步也是 ORM 框架里非常核心的部分

  • 相关阅读:
    【软考】9.1 顺序表/链表/栈和队列
    PTA 7-251 汉诺塔问题
    python 图像处理(一阶梯度图像和角度图像)
    pytorch深度学习实战lesson10
    人机交互的周期性与非周期性
    [Vue3]简易时间轴组件
    Java基于微信小程序的一起考研学习平台
    HarmonyOS Stage模型 用程序运行切换 验证UIAbility 启动模式(下) 验证:specified启动模式 Ability间切换
    Java安全之Mojarra JSF反序列化
    【Java从入门到精通 07】:面向对象编程(基础部分)
  • 原文地址:https://blog.csdn.net/cativen/article/details/126872193