mybatis是怎么拿sqlSession

在 上一篇的时候,我们的SqlSessionFactoryBuilder已经从xml文件中解析出了Configuration并且返回了sessionFactory。

然后我们要从session;中拿到sqlSession

public class DefaultSqlSessionFactory implements SqlSessionFactory {private final Configuration configuration;@Overridepublic SqlSession openSession() {//默认情况下ExecutorType是ExecutorType.SIMPLE类型的return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {Transaction tx = null;try {//获取配置的环境信息final Environment environment = configuration.getEnvironment();//获取environment中的TransactionFactoryfinal TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);//生成Transactiontx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//生成Executor(重要,之后的查询都得靠它)final Executor executor = configuration.newExecutor(tx, execType);//返回DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor, autoCommit);} catch (Exception e) {closeTransaction(tx); // may have fetched a connection so lets call close()throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
}//DefaultSqlSession类的组成,其实新建的时候就只是把他的字段赋值而已
public class DefaultSqlSession implements SqlSession {private Configuration configuration;private Executor executor;private boolean autoCommit;private boolean dirty;private List<Cursor<?>> cursorList;
}public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {this.configuration = configuration;this.executor = executor;this.dirty = false;this.autoCommit = autoCommit;}

上面分析的是到拿到sqlSession为止,重点其实不是在上面这里,因为到上面为止,其实主要的功能只是将配置的信息解析成我们要的类,然后进行初始化赋值。

Mapper的实现原理

下面我们从SqlSession中拿到mapper,并执行方法其实才是,你感觉到mybatis框架开始帮我们做事的开始。

    public static void main(String[] args) {//拿到SqlSessionSqlSession sqlsession = MybatisUtil.getSqlsession();//拿mapperTDemoMapper mapper = sqlsession.getMapper(TDemoMapper.class);//调用mapper的方法List<TDemo> all = mapper.getAll();for (TDemo item : all)System.out.println(item);}

因为我们在项目中的TDemoMapper只是一个接口,并没有实现这个接口方法,但是为什么我们在调用这个接口方法的时候就可以得到返回结果呢?mybatis究竟做了什么?

首先我们回到之前解析Mapper的语句

mapperElement(root.evalNode("mappers"));private void mapperElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {if ("package".equals(child.getName())) {String mapperPackage = child.getStringAttribute("name");configuration.addMappers(mapperPackage);} else {String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if (resource != null && url == null && mapperClass == null) { //根据resource解析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) {//根据url 解析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) {//根据mapperClass解析//首先通过mapperClass的路径,生成mapperClass的接口类Class<?> mapperInterface = Resources.classForName(mapperClass);//降mapperClass加入到configuration中去configuration.addMapper(mapperInterface);} else {throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}}//Configuration类下public <T> void addMapper(Class<T> type) {mapperRegistry.addMapper(type);}//MapperRegistry类下
public class MapperRegistry {private final Configuration config;private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();//最终调用这个方法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 {//将接口包装成MapperProxyFactory类放入knownMappers中(knownMappers就是存放我们的mapper接口的)knownMappers.put(type, new MapperProxyFactory<T>(type));// It's important that the type is added before the parser is run// otherwise the binding may automatically be attempted by the// mapper parser. If the type is already known, it won't try.//通过这个builder来解析mapper的statement。(把mapper和mapper.xml文件相关联,方法名与xml中的id相关联,为了之后调用的时候能找到的语句)MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);//开始解析parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}
}//MapperAnnotationBuilder类中public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {//通过xml文件解析loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();//获得接口的方法(为了获取方法上的注解,通过注解的方式来让方法于sql语句相关联)Method[] methods = type.getMethods();for (Method method : methods) {try {// issue #237if (!method.isBridge()) {//具体的解析过程parseStatement(method);}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}

具体的调用过程就不细跟了,无非就是获取节点,获取属性值,(或者是获取方法,然后获取注解信息),巴拉巴拉……然后设置到configuration中。
上面要注意的点是,若既配置xml又配置注解的情况下,注解会覆盖xml,原因非常简单,源码中注解解析在xml解析后面,然后覆盖的情况是,他们有相同的namespace+id。
然后我们继续我们的主线任务,就是mapper的设计架构。从上面我们可以知道,configuration中有一个MapperRegistry类型的字段mapperRegistry,其中有一个字段叫knownMappers,knownMappers里面存着key为接口类型,值为MapperProxyFactory的。

//我们在调用TDemoMapper mapper = sqlsession.getMapper(TDemoMapper.class);的时候
//先调用DefaultSqlSession类下的@Overridepublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);}
//然后调用Configuration类下的public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}//最后调用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 {//实际生成是这段代码,通过mapperProxyFactory来生成实例对象return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new BindingException("Error getting mapper instance. Cause: " + e, e);}}//实际调用类
public class MapperProxyFactory<T> {private final Class<T> mapperInterface;private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();public T newInstance(SqlSession sqlSession) {//实例化一个代理类final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);//通过这个函数实例化return newInstance(mapperProxy);}protected T newInstance(MapperProxy<T> mapperProxy) {//动态代理的基本操作(说明最终实现方式是动态代理)return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, 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;}//动态代理中最重要的方法invoke@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {//如果是Object中的方法就不走下面的代理了,直接执行(比如toString,hashCode)return method.invoke(this, args);} else if (isDefaultMethod(method)) {//如果不是静态方法而且不是抽象方法,则不增强方法return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}//实际我们的mapper接口的方法走的逻辑就是下面这2条final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}
}

总结

我们通过sqlSession获得mapper方法,而sqlSession从configuration中的mapperRegistry中获取MapperProxyFactory对象,在通过MapperProxyFactory对象的newInstance方法得到MapperProxy的动态代理实例对象。

我们使用的mapper其实是通过MapperProxy动态代理,在运行时候生成的一个新的对象进行方法增强的,里面的接口方法都会通过下面2个语句进行数据库的操作,以及后续对数据的处理

    final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);12

这两条语句其实包含对访问数据库对象的创建,访问数据库到得到数据库返回数据后的处理等内容,非常复杂,本篇就到此为止。

Mybatis源码分析之(三)mapper接口底层原理(为什么不用写方法体就能访问到数据库)相关推荐

  1. Anbox源码分析(三)——Anbox渲染原理(源码分析)

    Anbox源码分析(三) 上一篇,我们介绍了Anbox视频渲染的原理,这一篇,我们从源码入手,更深入的理解Anbox与渲染的机制和原理 session manager入口 session manage ...

  2. Mybatis源码分析--关联表查询及延迟加载原理(二)

    在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...

  3. Mybatis源码分析(三)通过实例来看typeHandlers

    一.案例分析 在日常开发中,我们肯定有对日期类型的操作.比如订单时间.付款时间等,通常这一类数据在数据库以 datetime类型保存.如果需要在页面上展示此值,在Java中以什么类型接收它呢? 在不执 ...

  4. MyBatis源码分析(三):MyBatis初始化(配置文件读取和解析)

    一. 介绍MyBatis初始化过程 项目是简单的Mybatis应用,编写SQL Mapper,还有编写的SqlSessionFactoryUtil里面用了Mybatis的IO包里面的Resources ...

  5. MyBatis源码分析(一)MyBatis整体架构分析

    文章目录 系列文章索引 一.为什么要用MyBatis 1.原始JDBC的痛点 2.Hibernate 和 JPA 3.MyBatis的特点 4.MyBatis整体架构 5.MyBatis主要组件及其相 ...

  6. bytebuddy实现原理分析 源码分析 (三)- advice 详解

    advice详解 八.advice 8.1 AsmVisitorWrapper 8.1.1 ForDeclareFields 8.1.1.1 Entry 8.1.1.2 DispatchingVisi ...

  7. springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三)

    springboot集成mybatis源码分析-mybatis的mapper执行查询时的流程(三) 例: package com.example.demo.service;import com.exa ...

  8. 我的架构梦:(三)MyBatis源码分析

    mybatis的源码分析 一.传统方式源码分析 二.Mapper代理方式源码分析 三.MyBatis源码中涉及到的设计模式 一.传统方式源码分析 分析之前我们来回顾下传统方式的写法: /*** 传统方 ...

  9. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

最新文章

  1. mysql性能优化1
  2. 快速排序(Python实现)
  3. 数据结构与算法笔记(七)—— 选择排序
  4. VC中GetLastError()获取错误信息的使用,以及错误代码的含义
  5. 入门学Java,要学哪些开发工具呢?
  6. hdu--4902--线段树
  7. Java应用性能调优工具介绍及实践
  8. java 异常 中英文_史上最全的Java中所有Exception异常中英文对照
  9. pl sql练习(3)
  10. JTable 学习一
  11. 记录word的页码问题-页码分节、罗马数字页码
  12. system verilog基础知识总结与复习(随机化)
  13. 容量 Byte、KB、MB、GB、TB、PB、EB、ZB、YB、NB、DB、CB、XB
  14. 【更好用的单片机】Stduino学习(三十三)面包板模块
  15. CT影像文件格式DICOM详解
  16. VS2017 打包exe,msi文件
  17. 艾兰岛编辑器-实体模板
  18. 解码百度核心:移动的无色墙,AI的无形剑
  19. 周围剃光头顶留长发型_为什么很多秃头的人宁可留周围一圈头发也不剃成光头?...
  20. 8.12 Python web前端 HTML认识

热门文章

  1. vs怎么更改编译的堆空间_再见吧 buildSrc, 拥抱 Composing builds 提升 Android 编译速度...
  2. opencv机器学习线性回归_机器学习(线性回归(二))
  3. php如何输出复选框的值,php 怎么输出复选框呢?
  4. java 共享锁 独占锁_java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁...
  5. 密码学常用的算法填充模式_密码学的操作模式
  6. 如何打印出给定尺寸的方格_打印给定号码的表格| 8086微处理器
  7. android scrollview焦点,scrollview里面的edittext,当它获得焦点时如何滚动到edittext
  8. ubutun 更换网络源_Ubuntu 更换源
  9. c# 用空格分割字符串_C#| 左用空格填充字符串
  10. 从JVM入手,聊聊Java的学习和复习!