手撸Mybatis(一)——代理mapper

server/2024/9/24 13:19:03/

引言

最近刚写完毕设,闲来无事,看到网上有一个手撸Mybatis的教程,于是想自己实现一个简易版的Mybatis。
本专栏的源码:https://gitee.com/dhi-chen-xiaoyang/yang-mybatis

创建简单的映射器代理工厂

在使用mybatis的时候,我们一般只需要定义mapper的接口,并添加相应的@Mapper注解,然后实现对应的xml文件即可,而不需要对mapper接口进行具体的实现。其实本质上,这些mapper接口是有实现的,但不是我们手动通过implement来实现,而是通过代理的方式进行实现。因此,对于Mybatis的手撸,首先要关注的,就是如何对mapper进行代理。
首先我们定义一个MapperProxy,该类实现InvocationHandler接口,通过实现该接口,实现动态代理。这里有两个属性——sqlSession和mapperInterface,其中,sqlSession是用来模拟执行sql语句的。

package com.yang.mybatis.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;public class MapperProxy<T> implements InvocationHandler {private Map<String, Object> sqlSession ;private Class<T> mapperInterface;public MapperProxy(Map<String, Object> sqlSession, Class<T> mapperInterface) {this.sqlSession = sqlSession;this.mapperInterface = mapperInterface;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);}String key = this.mapperInterface.getName() + "." + method.getName();return sqlSession.get(key);}
}

当我们在执行mapper的某个方法时,最终会进入到invoke方法,并通过sqlSession来获取模拟值。
接着我们定义MapperProxyFactory,每一个mapper都有一个MapperProxyFactory与之相对应,然后newInstance方法接收模拟的sql结果,并通过Proxy.newProxyInstance创建动态代理对象。

package com.yang.mybatis.proxy;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, Object> sqlSession) {MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),new Class[]{mapperInterface},mapperProxy);}
}

最后,我们先创建一个IUserMapper类

package com.yang.mybatis.test;public interface IUserMapper {String queryUserName(Integer id);Integer queryUserAge(Integer id);
}

然后创建对应的测试方法

package com.yang.mybatis.test;import com.yang.mybatis.proxy.MapperProxyFactory;import java.util.HashMap;
import java.util.Map;public class Main {public static void main(String[] args) {MapperProxyFactory<IUserMapper> userDaoMapperProxyFactory = new MapperProxyFactory<>(IUserMapper.class);Map<String, Object> sqlSession = new HashMap<>();sqlSession.put("com.yang.mybatis.test.IUserMapper.queryUserName", "模拟查询用户名");sqlSession.put("com.yang.mybatis.test.IUserMapper.queryUserAge", 1);IUserMapper iUserMapper = userDaoMapperProxyFactory.newInstance(sqlSession);System.out.println(iUserMapper.queryUserAge(1));System.out.println(iUserMapper.queryUserName(1));}
}

运行结果如下:
image.png

实现映射器的注册和使用

像上述的这种方法,我们每次要获取一个Mapper,就要new一个对应的MapperProxyFactory,这样不太方便。一般情况下,mapper是放在同一个包下的,那么我们可以通过扫描包,来初始化MapperProxyFactory。至于上一节的sqlSession,因为我们目前是模拟数据,所以在初始化过程中,把这些模拟数据要随便mock住就行。
因为要扫描包获取包下的Class类,我们先添加hutool-all,以便通过其提供的ClassScanner获取包

   <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.12</version></dependency>

然后,我们修改MapperProxyFactory

package com.yang.mybatis.proxy;import cn.hutool.core.lang.ClassScanner;import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;public class MapperProxyFactory {private Map<Class, MapperProxy> mapperProxyMap = new HashMap<>();public MapperProxyFactory(String packageName) {// 扫描包ClassScanner scanner = new ClassScanner(packageName);Set<Class<?>> mapperTypes = scanner.scan();for (Class<?> mapperType : mapperTypes) {if (!mapperType.isInterface()) {// 只对接口进行处理continue;}Map<String, String> mockSqlSession = mockSqlSession(mapperType);MapperProxy mapperProxy = new MapperProxy(mockSqlSession, mapperType);mapperProxyMap.put(mapperType, mapperProxy);}}public Object newInstance(Class mapperType) {MapperProxy mapperProxy = mapperProxyMap.get(mapperType);return Proxy.newProxyInstance(mapperType.getClassLoader(),new Class[]{mapperType},mapperProxy);}private Map<String, String> mockSqlSession(Class mapperType) {Map<String, String> sqlSession = new HashMap<>();for (Method method : mapperType.getMethods()) {String methodName = method.getName();String key = mapperType.getName() + "." + methodName;sqlSession.put(key, key);}return sqlSession;}
}

最后进行测试:

  public static void main(String[] args) {MapperProxyFactory mapperProxyFactory = new MapperProxyFactory("com.yang.mybatis.test");IUserMapper iUserMapper = (IUserMapper) mapperProxyFactory.newInstance(IUserMapper.class);System.out.println(iUserMapper.queryUserName(1));}

测试结果如下:
image.png


http://www.ppmy.cn/server/30346.html

相关文章

2024年北京高校数学建模校际联赛竞赛B题

B题 铁道线路动态检测数据分析 铁道线路设备是铁路运输业的基础设备&#xff0c;它常年裸露在大自然中&#xff0c;经受着风雨冻融和列车荷载的作用&#xff0c;轨道几何尺寸不断变化&#xff0c;路基及道床不断产生变形&#xff0c;钢轨、联结零件及轨枕不断磨损&#xff0c…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之九 简单进行嘴巴检测并添加特效的功能实现

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单人脸检测/识别实战案例 之九 简单进行嘴巴检测并添加特效的功能实现 目录

什么是trace系统?

Trace系统&#xff0c;也常被称为分布式追踪系统&#xff0c;是一种用于监控、分析和优化复杂分布式系统&#xff08;如微服务架构&#xff09;中服务调用的工具。随着现代应用程序向微服务架构的转变&#xff0c;一个用户请求可能需要跨越多个服务来完成。这种架构带来了更好的…

MFC 列表控件删除实例

1、本程序基于前期我的博客文章《MFC下拉菜单打钩图标存取实例&#xff08;源码下载) 》 2、程序功能选中列表控件某一项&#xff0c;删除按钮由禁止变为可用&#xff0c;点击删除按钮&#xff0c;选中的项将删除。 3、首先在主界面添加一个删除参数按钮。 4、在myDlg.cpp 文件…

安卓adb 命令查看程序日志

gcat日志导出到文件 在Android设备上&#xff0c;你可以使用logcat命令将日志导出到文件中。打开终端或者命令行工具&#xff0c;然后输入以下命令&#xff1a; adb logcat -d > logcat.txt这条命令会将当前设备的logcat日志输出到名为logcat.txt的文件中。-d参数是用来确…

像人一样开车的自动驾驶:商汤绝影大模型北京车展首秀

说起自动驾驶&#xff0c;相信大家都不会陌生。但是大家有没有想过&#xff0c;在人工智能和大模型的帮助下&#xff0c;自动驾驶已经可以像人一样开车了&#xff1f; 在2024北京车展上&#xff0c;首次参展的商汤绝影为来宾们展示了一系列引领行业的创新技术。其中&#xff0c…

代码审计的基础思路

介绍 顾名思义就是检查源代码中的安全缺陷&#xff0c;检查程序源代码是否存在安全隐患&#xff0c;或者有编码不规范的地方&#xff0c;通过自动化工具或者人工审查的方式&#xff0c;对程序源代码逐条进行检查和分析&#xff0c;发现这些源代码缺陷引发的安全漏洞&#x…

ue引擎游戏开发笔记(25)——增添特效

1需求分析 为了让游戏更真实&#xff0c;需要给游戏增添一些特效&#xff0c;例如敌人被摧毁时出现一个爆炸特效&#xff0c;子弹发射时有尾烟等等。 2.操作实现 1.与声音类似&#xff0c;首先也需要在需要的地方先建立声明&#xff0c;以便能在引擎中进行设置&#xff0c; 2.…