转: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. Colourful Rectangle【扫描线】

    题目链接 很明显的可以发现是一个扫描线的问题,但是怎么处理区域呢,发现只有三种颜色,也就是最多也就是7种状态,那么我们可以进行一个状态压缩即可. 但是,在向上pushup的时候,存在我们要以子树的状态 ...

  2. Rsync+inotify搭建使用

    ## Rsync搭建 ### 1.1 环境准备 ``` Rsync-Server 192.168.1.174 Client-Rsync 192.168.1.173 服务启动用户都是root,客户端的用 ...

  3. 解决bug:sprongboot2整合shiro,swagger2页面样式加载不出来问题

    问题如题: 解决思路,把shiro拦截去掉之后发现swagger-ui.html页面接在的资源如下: 因此可以推断拦截器拦截了 "/swagger-resources" " ...

  4. redis 哨兵配置文件解读sentinel.conf

    # Example sentinel.conf # port <sentinel-port>port 8001 # 守护进程模式daemonize yes # 指明日志文件名logfile ...

  5. C# Winform 窗体界面”假死”后台线程阻塞 解决办法–BeginInvoke

    原文:C# Winform 窗体界面"假死"后台线程阻塞 解决办法–BeginInvoke 这个方法可以用在任何后台任务耗时较长,造成界面“假死”界面控件不更新的情况. 比如要要执 ...

  6. Python内置函数(19)-slice

    官方文档 class slice(stop) class slice(start, stop[, step]) Return a slice object representing the set o ...

  7. Redis线上环境做Keys匹配操作!你可以离职了!

    转自:https://blog.csdn.net/bntx2jsqfehy7/article/details/84207884一.一个新闻 新闻内容如下:php工程师执行redis keys * 导致 ...

  8. Sass-注释

    注释对于一名程序员来说,是极其重要,良好的注释能帮助自己或者别人阅读源码.在 Sass 中注释有两种方式,我暂且将其命名为: 1.类似 CSS 的注释方式,使用 ”/* ”开头,结属使用 ”*/ ”2 ...

  9. HTML5 canvas绘制图片

    demo.js window.onload=function() { createcanvas(); drawImage(); } function createcanvas() { var CANV ...

  10. cookie记住账户密码

    //cookie保存路径 String autologin=request.getParameter("autologin"); String path = request.get ...