转: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及方法执行详解的更多相关文章

  1. 【转】Mybatis 3.1中 Mapper XML 文件 的学习详解

    MyBatis 真正的力量是在映射语句中.这里是奇迹发生的地方.对于所有的力量,SQL 映射的 XML 文件是相当的简单.当然如果你将它们和对等功能的 JDBC 代码来比较,你会发现映射文件节省了大约 ...

  2. Mybatis 3.1中 Mapper XML 文件 的学习详解(转载)

    MyBatis 真正的力量是在映射语句中.这里是奇迹发生的地方.对于所有的力量,SQL 映射的 XML 文件是相当的简单.当然如果你将它们和对等功能的 JDBC 代码来比较,你会发现映射文件节省了大约 ...

  3. MyBatis Mapper XML 文件 的学习详解

    MyBatis 真正的力量是在映射语句中.这里是奇迹发生的地方.对于所有的力量,SQL 映射的 XML 文件是相当的简单.当然如果你将它们和对等功能的 JDBC 代码来比较,你会发现映射文件节省了大约 ...

  4. 逆向工程生成的Mapper.xml以及*Example.java详解

    逆向工程生成的接口中的方法详解 在我上一篇的博客中讲解了如何使用Mybayis逆向工程针对单表自动生成mapper.java.mapper.xml.实体类,今天我们先针对mapper.java接口中的 ...

  5. 2017.2.9 深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二)-----配置文件详解

    深入浅出MyBatis技术原理与实践-第八章 MyBatis-Spring(二) ------配置文件详解 8.2 MyBatis-Spring应用 8.2.1 概述 本文主要讲述通过注解配置MyBa ...

  6. C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解

    前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi的园友们速速动起来,跟着博主一起来学习吧.之前分享过一篇 C#进阶系列——WebApi接口传参不再困惑:传参详解  ...

  7. 集合类 Contains 方法 深入详解 与接口的实例

    .Net 相等性:集合类 Contains 方法 深入详解 http://www.cnblogs.com/ldp615/archive/2009/09/05/1560791.html 1.接口的概念及 ...

  8. (转)C# WebApi 接口返回值不困惑:返回值类型详解

    原文地址:http://www.cnblogs.com/landeanfen/p/5501487.html 正文 前言:已经有一个月没写点什么了,感觉心里空落落的.今天再来篇干货,想要学习Webapi ...

  9. [转]C#进阶系列——WebApi 接口返回值不困惑:返回值类型详解

    本文转自:http://www.cnblogs.com/landeanfen/p/5501487.html 阅读目录 一.void无返回值 二.IHttpActionResult 1.Json(T c ...

随机推荐

  1. 爬虫中GET方法应用基本模型

    根据get方法,更改界面url从而获取信息 GET请求URL附带查询参数 POST请求保存在form表单中 分析百度贴吧url特点: 分析url https://tieba.baidu.com/f是贴 ...

  2. Oracle11g安装步骤

    plsql安装等:https://blog.csdn.net/li66934791/article/details/83856225      https://www.cnblogs.com/gaoz ...

  3. 洛谷 P1049 装箱问题(01背包)

    一道水题,但看到好久没有发博客了,再一看是一道noip普及组t4,就做了. 题目链接 https://www.luogu.org/problemnew/show/P1049 解题思路 一道裸的01背包 ...

  4. 57-python基础-python3-集合-集合常用方法-添加元素-add()-update()

    添加元素-add()-update() 1-add() add()用于增加一个元素值,原值修改,无返回值. 2-update()用于添加一个可迭代的对象,原值修改,无返回值. 下面依次向集合添加可迭代 ...

  5. Python之带有外部状态的生成器函数

    带有外部状态的生成器函数,也就是你的生成器暴露外部状态给用户解决: 定义一个类,然后把生成器函数放到 __iter__() 方法中过去 定义一个类,然后把生成器函数放到 __iter__() 方法中过 ...

  6. Java实现二分法(折半)查找数组中的元素

    二分查找 算法思想:又叫折半查找,要求待查找的序列有序.每次取中间位置的值与待查关键字比较,如果中间位置的值比待查关键字大,则在前半部分循环这个查找的过程,如果中间位置的值比待查关键字小,则在后半部分 ...

  7. Docker Swanm集群配置

    首先 可以用ContOS虚拟机   克隆  5个虚拟机,注意(克隆主机必须装了Docker,克隆后,克隆机都会有Docker) 配置 网络 克隆CentOS虚拟机 最后和到如下结果 打开2377端口 ...

  8. ArrayList、LinkedList、Vector区别

    ArrayList.LinkedList.Vector均为可伸缩数组,即可以动态改变长度的数组. 比较ArrayList和Vector: 1. 共同点: ArrayList和Vector都是基于Obj ...

  9. python正则表达式 re (二)escape

    背景: 在使用python的过程中,你肯定对转义字符的使用苦恼过,因为有的时候我们需要使用一些特殊符号如”$ * . ^”等的原意,有时候需要被转义后的功能,并且转义字符地使用很繁琐,容易出错,那拯救 ...

  10. 牛客小白月赛16 F 小石的妹子 (线段树)

    链接:https://ac.nowcoder.com/acm/contest/949/F来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...