mybatis如何通过接口查找对应的mapper.xml及方法执行详解
转:http://www.jb51.net/article/116402.htm
本文主要介绍的是关于mybatis通过接口查找对应mapper.xml及方法执行的相关内容,下面话不多说,来看看详细的介绍:
在使用mybatis的时候,有一种方式是
|
1
|
BookMapper bookMapper = SqlSession().getMapper(BookMapper.class) |
获取接口,然后调用接口的方法。只要方法名和对应的mapper.xml中的id名字相同,就可以执行sql。
那么接口是如何与mapper.xml对应的呢?
首先看下,在getMapper()方法是如何操作的。
在DefaultSqlSession.Java中调用了configuration.getMapper()
|
1
2
3
|
public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } |
在Configuration.java中调用了mapperRegistry.getMapper(type, sqlSession);
|
1
2
3
|
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } |
下面重点来了,在MapperRegistry.java中实现了动态代理
|
1
2
3
4
5
6
7
8
9
10
|
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();共有两个重载。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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的解析,这里先不赘述,
|
1
|
mapperElement(root.evalNode("mappers")); |
|
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
|
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()的顺序。
同时在改方法中还有一个方法很重要
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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
|
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
|
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中进行方法的执行
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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及方法执行详解的更多相关文章
- 【转】Mybatis 3.1中 Mapper XML 文件 的学习详解
MyBatis 真正的力量是在映射语句中.这里是奇迹发生的地方.对于所有的力量,SQL 映射的 XML 文件是相当的简单.当然如果你将它们和对等功能的 JDBC 代码来比较,你会发现映射文件节省了大约 ...
- Mybatis 3.1中 Mapper XML 文件 的学习详解(转载)
MyBatis 真正的力量是在映射语句中.这里是奇迹发生的地方.对于所有的力量,SQL 映射的 XML 文件是相当的简单.当然如果你将它们和对等功能的 JDBC 代码来比较,你会发现映射文件节省了大约 ...
- MyBatis Mapper XML 文件 的学习详解
MyBatis 真正的力量是在映射语句中.这里是奇迹发生的地方.对于所有的力量,SQL 映射的 XML 文件是相当的简单.当然如果你将它们和对等功能的 JDBC 代码来比较,你会发现映射文件节省了大约 ...
- 逆向工程生成的Mapper.xml以及*Example.java详解
逆向工程生成的接口中的方法详解 在我上一篇的博客中讲解了如何使用Mybayis逆向工程针对单表自动生成mapper.java.mapper.xml.实体类,今天我们先针对mapper.java接口中的 ...
- 2017.2.9 深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二)-----配置文件详解
深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二) ------配置文件详解 8.2 MyBatis-Spring应用 8.2.1 概述 本文主要讲述通过注解配置MyBa ...
- C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解 ...
- 集合类 Contains 方法 深入详解 与接口的实例
.Net 相等性:集合类 Contains 方法 深入详解 http://www.cnblogs.com/ldp615/archive/2009/09/05/1560791.html 1.接口的概念及 ...
- (转)C# WebApi 接口返回值不困惑:返回值类型详解
原文地址:http://www.cnblogs.com/landeanfen/p/5501487.html 正文 前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi ...
- [转]C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解
本文转自:http://www.cnblogs.com/landeanfen/p/5501487.html 阅读目录 一.void无返回值 二.IHttpActionResult 1.Json(T c ...
随机推荐
- 爬虫中GET方法应用基本模型
根据get方法,更改界面url从而获取信息 GET请求URL附带查询参数 POST请求保存在form表单中 分析百度贴吧url特点: 分析url https://tieba.baidu.com/f是贴 ...
- Oracle11g安装步骤
plsql安装等:https://blog.csdn.net/li66934791/article/details/83856225 https://www.cnblogs.com/gaoz ...
- 洛谷 P1049 装箱问题(01背包)
一道水题,但看到好久没有发博客了,再一看是一道noip普及组t4,就做了. 题目链接 https://www.luogu.org/problemnew/show/P1049 解题思路 一道裸的01背包 ...
- 57-python基础-python3-集合-集合常用方法-添加元素-add()-update()
添加元素-add()-update() 1-add() add()用于增加一个元素值,原值修改,无返回值. 2-update()用于添加一个可迭代的对象,原值修改,无返回值. 下面依次向集合添加可迭代 ...
- Python之带有外部状态的生成器函数
带有外部状态的生成器函数,也就是你的生成器暴露外部状态给用户解决: 定义一个类,然后把生成器函数放到 __iter__() 方法中过去 定义一个类,然后把生成器函数放到 __iter__() 方法中过 ...
- Java实现二分法(折半)查找数组中的元素
二分查找 算法思想:又叫折半查找,要求待查找的序列有序.每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分 ...
- Docker Swanm集群配置
首先 可以用ContOS虚拟机 克隆 5个虚拟机,注意(克隆主机必须装了Docker,克隆后,克隆机都会有Docker) 配置 网络 克隆CentOS虚拟机 最后和到如下结果 打开2377端口 ...
- ArrayList、LinkedList、Vector区别
ArrayList.LinkedList.Vector均为可伸缩数组,即可以动态改变长度的数组. 比较ArrayList和Vector: 1. 共同点: ArrayList和Vector都是基于Obj ...
- python正则表达式 re (二)escape
背景: 在使用python的过程中,你肯定对转义字符的使用苦恼过,因为有的时候我们需要使用一些特殊符号如”$ * . ^”等的原意,有时候需要被转义后的功能,并且转义字符地使用很繁琐,容易出错,那拯救 ...
- 牛客小白月赛16 F 小石的妹子 (线段树)
链接:https://ac.nowcoder.com/acm/contest/949/F来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...