SqlSession可以说是整个MyBatis的重中之重,在SqlSession中涉及到前一篇四大对象:Executor、StatementHandler、ParameterHandler、ResultHandler,所以在此先只对SqlSession有一个大概的了解。

在代码中我们可以看到当我们构造出一个SqlSession实例过后,可以通过SqlSession构造出Mappper映射器。UserMapper是一个接口,那么我们可以肯定的是,它一定是用了Java的动态代理生成了一个代理类。

SqlSession sqlSession = SessionFactory.getSqlSession(resource);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

接着上一篇讲到通过DefaultSqlSessionFactory可以得到一个SqlSession的实例DefaultSqlSession。

通过源代码可以发现,SqlSessionManger又实现了SqlSession,在上一节中可知SqlSessionManager同样也继承了SqlSessionFactory接口。我们把它们结合起来看看。

看来这个SqlSessionManager有点神奇,同时继承SqlSession和SqlSessionFactory。从名字上来看好像是管理SqlSession的,这里不讨论,随着源代码的阅读相信我们能逐步清晰,包括整个包的结构。

回到DefaultSqlSession中来。在SqlSession接口中提供了很多方法,用于我们的增删改查,这在旧版的MyBatis或者iBatis中常常所使用的,我们现在大多直接使用xml配置文件以达到更加灵活的效果。所以我们将注意力放在getMapper方法上。

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

注意UserMapper仅仅是一个接口,这里会涉及到Java的动态代理,所以得要有一定的基础才能读懂。

通过打断点调试我们可以发现确实产生了一个叫MapperProxy的代理类。

下面是DefaultSqlSession的getMapper方法:

//org.apache.ibatis.session.default.DefaultSqlSession
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}

看起来语法有点奇怪,这是一个泛型方法。看来是调用了Configuration的getMapper方法,还不是DefaultSqlSession实现了getMapper。接着再看Configuration的getMapper方法:

//org.apache.ibatis.session.Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}

Configuration.getMapper一共两个参数,一个是Class类型,一个是SqlSession,在DefaultSqlSession.getMapper调用Configuration.getMapper时,将传递进来的Class类型参数和其本身传递给了Configuration.getMapper。此时还不是在Configuration中实现了getMapper,看来还是一个叫做mapperRegistry的变量。

//org.apache.ibatis.session.Configuration
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

看着名字好像是注册Mapper映射器的地方,想来也是,既然要得到Mapper的映射,那么所有的Mapper都要一个地方去注册(在我们的mybytis-config.xml里),注册好过后需要的时候再去查找是否已经注册,那么就是MapperRegistry,所以取一个好的变量名是非常重要的。

 //org.apache.ibatis.binding.MapperRegistry
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);
}
}

在第3行代码中试图从一个叫knownMappers的变量取出MapperProxyFactory。这个knownMapper的定义:

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

既然能用get方法取,那说明就有add方法咯?果不其然我们在MapperRegistry类中发现了public <T> void addMapper(Class<T> type)方法,那么是在哪里调用的这个方法呢?

我们来重新理一理。

使用MyBatis的第一步是配置mybatis-config.xml,配置好过后,mybatis-config跑起来的第一步也一定是首先解析xml配置文件,将解析好的配置文件各个配置参数放入Configuration对象中,包括Mapper的配置,所以应该是在解析xml文件的某个类中解析过来后调用Configuration的方法将mapper放置到MapperRegister中。事实也的确如此,有兴趣可以跟踪下代码看看。回到MapperRegistry.getMapper的方法中。

当我们一切正确时,我们就能获取到一个MapperProxyFactory实例。想必MapperProxy代理类的生成正是通过MapperProxyFactory工厂类构建的,即第8行代码。进入MapperProxyFactory类。

//org.apache.ibatis.binding.MapperProxyFactory
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}

在这里终于看到了MapperProxy代理类,是通过sqlSession、mapperInterface、mechodCache三个参数构造的。

newInstance有一个重载方法:

//org.apache.ibatis.binding.MapperProxyFactory
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

终于是走到头了,这里就是返回的一个代理类实例。最后来看看MapperProxy。

MapperProxy是一个重要的类,所以我们将其代码全部贴出:

 //org.apache.ibatis.binding.MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
} @Override
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;
} }

要使用Java的动态代理就必须得实现InvocationHandler接口,在第17行代码中首先判断代理对象是一个接口还是一个类,显然我们没有对mapper接口进行任何实现,那么它将生成一个MapperMethod对象,接着调用其execute方法,把sqlSession和参数传递进去。

MyBatis源码解读(2)——MapperProxy的更多相关文章

  1. MyBatis源码解读(3)——MapperMethod

    在前面两篇的MyBatis源码解读中,我们一路跟踪到了MapperProxy,知道了尽管是使用了动态代理技术使得我们能直接使用接口方法.为巩固加深动态代理,我们不妨再来回忆一遍何为动态代理. 我相信在 ...

  2. spring IOC DI AOP MVC 事务, mybatis 源码解读

    demo https://gitee.com/easybao/aop.git spring DI运行时序 AbstractApplicationContext类的 refresh()方法 1: pre ...

  3. Mybatis源码解读-SpringBoot中配置加载和Mapper的生成

    本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载. 建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此 ...

  4. MyBatis源码解读之延迟加载

    1. 目的 本文主要解读MyBatis 延迟加载实现原理 2. 延迟加载如何使用 Setting 参数配置 设置参数 描述 有效值 默认值 lazyLoadingEnabled 延迟加载的全局开关.当 ...

  5. Mybatis源码解读-插件

    插件允许对Mybatis的四大对象(Executor.ParameterHandler.ResultSetHandler.StatementHandler)进行拦截 问题 Mybatis插件的注册顺序 ...

  6. 【转】Mybatis源码解读-设计模式总结

    原文:http://www.crazyant.net/2022.html?jqbmtw=b90da1&gsjulo=kpzaa1 虽然我们都知道有26个设计模式,但是大多停留在概念层面,真实开 ...

  7. Mybatis源码解读-设计模式总结

    虽然我们都知道有26个设计模式,但是大多停留在概念层面,真实开发中很少遇到,Mybatis源码中使用了大量的设计模式,阅读源码并观察设计模式在其中的应用,能够更深入的理解设计模式. Mybatis至少 ...

  8. MyBatis源码解读(1)——SqlSessionFactory

    在前面对MyBatis稍微有点了解过后,现在来对MyBatis的源码试着解读一下,并不是解析,暂时定为解读.所有对MyBatis解读均是基于MyBatis-3.4.1,官网中文文档:http://ww ...

  9. MyBatis源码解读(4)——SqlSession(上)

    在上一篇博客中提到MyBatis是如何实现代理类MapperProxy,并抛出了一个问题--是怎么执行一个具体的sql语句的,在文末中提到了MapperMethod的execute采用命令模式来判断是 ...

  10. mybatis源码解读(一)——初始化环境

    本系列博客将对mybatis的源码进行解读,关于mybatis的使用教程,可以查看我前面写的博客——传送门. 为了便于后面的讲解,我们这里首先构造一个统一环境.也可以参考mybatis官网. 1.数据 ...

随机推荐

  1. (转)JAVA多线程和并发基础面试问答

    JAVA多线程和并发基础面试问答 原文链接:http://ifeve.com/java-multi-threading-concurrency-interview-questions-with-ans ...

  2. css3hover效果

    <!doctype html><html><head><meta charset="utf-8"><title>无标题文 ...

  3. 关于constraint的用法

    1.主键约束:要对一个列加主键约束的话,这列就必须要满足的条件就是非空因为主键约束:就是对一个列进行了约束,约束为(非空.不重复)以下是代码  要对一个列加主键,列名为id,表名为emp格式为:alt ...

  4. CF IndiaHacks 2016 F Paper task 后缀数组

    题目链接:http://codeforces.com/problemset/problem/653/F 大意是给出一个只包含'('和')'的括号串,求有多少不同的子串是合法的括号串 解法:对于每一个后 ...

  5. 使用SSH框架查出的实体集合用AJAX形式刷新到页面JOSONArray报异常

    所报异常:net.sf.json.JSONException: java.lang.reflect.InvocationTargetException 问题根源:数据库中查出的日期是java.sql. ...

  6. PHP 分支与循环

    一.概述: 上面一章我们讲解了PHP当中的运算符和表达式,通过上面的知识点我们就可以完成一些基本的运算操作了.但是涉及到一些比较复杂的逻辑,分支与循环就必不可少了.通过分支和循环的结合使用可以使业务更 ...

  7. checkbox的选中、全选、返选、获取所有选中的值、所有的值、单选全部时父选中

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  8. <CentOS7>如何设置hostname

    在CentOS/RHEL 7中,有个叫hostnamectl的命令行工具,它允许你查看或修改与主机名相关的配置: ceph@client-node ~]$ hostnamectlstatus Stat ...

  9. [Git]01 如何安装和配置

     简单地说,Git 究竟是怎样的一个系统呢? 请注意,接下来的内容非常重要,若是理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余.在开始学习 Git 的时候,请不要尝试把各种概 ...

  10. file_get_contents url

    file_get_contents (PHP 4 >= 4.3.0, PHP 5) file_get_contents — 将整个文件读入一个字符串 说明¶ string file_get_co ...