mybatis如何通过接口查找对应的mapper.xml及方法执行详解
发布时间 - 2026-01-11 01:55:11 点击率:次本文主要介绍的是关于mybatis通过接口查找对应mapper.xml及方法执行的相关内容,下面话不多说,来看看详细的介绍:

在使用mybatis的时候,有一种方式是
BookMapper bookMapper = SqlSession().getMapper(BookMapper.class)
获取接口,然后调用接口的方法。只要方法名和对应的mapper.xml中的id名字相同,就可以执行sql。
那么接口是如何与mapper.xml对应的呢?
首先看下,在getMapper()方法是如何操作的。
在DefaultSqlSession.Java中调用了configuration.getMapper()
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
在Configuration.java中调用了mapperRegistry.getMapper(type, sqlSession);
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
下面重点来了,在MapperRegistry.java中实现了动态代理
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
这个函数分两部分来看,首先是从map集合中获取接口代理,map集合的来源,第二部分获取代理后实例化,获取接口的方法,执行sql。
对于第一部分:集合的来源。
这个MapperRegistry.java中有个方法是addMappers();共有两个重载。
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
//通过包名,查找该包下所有的接口进行遍历,放入集合中
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
//解析包名下的接口
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
往上追溯该方法的调用是在SqlSessionFactory.build();时对配置文件的解析,其中对节点mappers的解析,这里先不赘述,
mapperElement(root.evalNode("mappers"));
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//使用package节点进行解析配置
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
//注册包下的接口
configuration.addMappers(mapperPackage);
} else {
//使用mapper节点
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
这是调用addMapper()的顺序。
同时在改方法中还有一个方法很重要
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
//根据接口名寻找同包下同名的xml或者mapper的namespace是该接口的xml
//找到对用的xml后进行解析mapper节点里面的节点
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
这是通过接口的全路径来查找对应的xml。这里有两种方式解析,也就是我们平常xml文件放置位置的两种写法。
第一种是不加namespace,把xml文件放在和接口相同的路径下,同时xml的名字与接口名字相同,如接口名为Student.java,xml文件为Student.xml。在相同的包下。这种当时可以不加namespace.
第二种是加namespace,通过namespace来查找对应的xml.
到这就是接口名和xml的全部注册流程。
下面再说下第二部分就是通过动态代理获取接口名字来对应xml中的id。
主要有两个类MapperProxyFactory.java和MapperProxy.java
对于MapperProxyFactory.java
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
//构造函数,获取接口类
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
//供外部调用
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
在MapperProxy.java中进行方法的执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
//方法的执行
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
至此,就是mybatis所有接口和xml的加载,以及通过动态代理来进行接口的执行的过程。
总结
以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。
# mybatis
# mapper.xml
# mapper接口
# mybatis的mapper.xml
# 详解Mybatis通用Mapper介绍与使用
# Java的MyBatis框架中对数据库进行动态SQL查询的教程
# MyBatis 执行动态 SQL语句详解
# mybatis mapper.xml中如何根据数据库类型选择对应SQL语句
# 这是
# 不加
# 第二部分
# 的是
# 是在
# 来了
# 放在
# 有个
# 相关内容
# 这就是
# 两种
# 有一种
# 遍历
# 是从
# 很重要
# 还有一个
# 来看看
# 这篇文章
# 有两种
# 谢谢大家
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
LinuxShell函数封装方法_脚本复用设计思路【教程】
JavaScript模板引擎Template.js使用详解
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法
香港服务器建站指南:免备案优势与SEO优化技巧全解析
canvas 画布在主流浏览器中的尺寸限制详细介绍
如何快速辨别茅台真假?关键步骤解析
html5如何实现懒加载图片_ intersectionobserver api用法【教程】
如何快速生成ASP一键建站模板并优化安全性?
,网页ppt怎么弄成自己的ppt?
详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)
如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程
如何在 Python 中将列表项按字母顺序编号(a.、b.、c. …)
Laravel如何获取当前登录用户信息_Laravel Auth门面使用与Session用户读取【技巧】
香港服务器网站生成指南:免费资源整合与高速稳定配置方案
php8.4header发送头信息失败怎么办_php8.4header函数问题解决【解答】
Laravel辅助函数有哪些_Laravel Helpers常用助手函数大全
个人网站制作流程图片大全,个人网站如何注销?
*服务器网站为何频现安全漏洞?
Laravel中的withCount方法怎么高效统计关联模型数量
网站建设要注意的标准 促进网站用户好感度!
Laravel怎么实现搜索高亮功能_Laravel结合Scout与Algolia全文检索【实战】
如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?
Windows10如何更改计算机工作组_Win10系统属性修改Workgroup
Laravel如何配置.env文件管理环境变量_Laravel环境变量使用与安全管理
在线制作视频网站免费,都有哪些好的动漫网站?
Laravel如何清理系统缓存命令_Laravel清除路由配置及视图缓存的方法【总结】
Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】
如何彻底删除建站之星生成的Banner?
在线制作视频的网站有哪些,电脑如何制作视频短片?
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
laravel怎么为API路由添加签名中间件保护_laravel API路由签名中间件保护方法
ChatGPT怎么生成Excel公式_ChatGPT公式生成方法【指南】
bootstrap日历插件datetimepicker使用方法
音乐网站服务器如何优化API响应速度?
浅析上传头像示例及其注意事项
Win11搜索栏无法输入_解决Win11开始菜单搜索没反应问题【技巧】
如何在阿里云ECS服务器部署织梦CMS网站?
如何在IIS7中新建站点?详细步骤解析
Laravel如何升级到最新的版本_Laravel版本升级流程与兼容性处理
微信小程序 闭包写法详细介绍
Laravel如何使用Gate和Policy进行授权?(权限控制)
打造顶配客厅影院,这份100寸电视推荐名单请查收
Laravel怎么实现验证码功能_Laravel集成验证码库防止机器人注册
如何在VPS电脑上快速搭建网站?
Swift开发中switch语句值绑定模式
Laravel API资源类怎么用_Laravel API Resource数据转换
Laravel怎么实现API接口鉴权_Laravel Sanctum令牌生成与请求验证【教程】
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
Laravel如何生成API文档?(Swagger/OpenAPI教程)

