Mybatis体系结构与工作原理

1、工作流程:

Mybatis体系结构与工作原理

       在MyBatis启动的时候,主要在解析配置文件,包括全局配置文件(Mybatis-config.xml)、映射器配置文件(Mapper.xml) Mapper包含了我们怎么控制MyBatis的行为和我们要对数据库下达的指令,也就是我们的动态SQL,及入参类型,返回值映射,程序会把这些信息解析成一个Configuration对象。

       在程序调用数据库操作Dao接口的时候,它在应用程序和数据库之间建立一次连接,就是创建了SqlSession对象。SqlSession对象由会话工厂SqlSessionFactory创建,SqlSessionFactory包含所有的配置信息,在容器初始化的时候由SqlSessionFactoryBuilder创建。

      在执行数据库操作的时候,主要是Executor对象执行操作,并将操作核心流程设定为映射输入的参数映射,StatementHandler处理适配器,执行数据库操作,映射输出的参数映射;

 

2,mybatis的jar包构成

     梳理清楚了整个mybatis的执行流程之后,我们再来看看其jar包,了解其实如何实现代码的;

Mybatis体系结构与工作原理

      annotations:注解

      binding:接口与statement的绑定关系,接口注册成的MapperProxy与最终通过MapperCommand执行SQL语句;

      builder: 解析SqlMap文件,包括:classPath解析、根据路径加载文件按照xml分层解析文件、SqlMap解析、Statement解析

      cache:缓存层

基本实现:缓存层的接口是Cache, 基本实现是PerpetualCache,一级缓存和二级缓存都使用这个实现,用HashMap保存缓存内容;

设计模式: 装饰器模式,通过装饰实现缓存功能的增强。

分      类:内部缓存分为功能性缓存类 和 淘汰策略类缓存(FifoCache,LruCahe、WeakCache、SoftCache);

缓存对象在什么时候被创建?什么时候被装饰?

      1,一级缓存:默认开启使用,

           作用域:sqlsession会话级别的,

           创建:BaseExector中进行创建,初始化 LocalCache,

      2,二级缓存:在全局配置中<setting>中 <cacheEnable>  开启,  namespace中 <cache/>;

           作用域:nameSpace级别,Mapper接口的级别,同一个接口的同一个方法相同参数就可以使用缓存

           创建:在BaseExector的装饰者 - CachingExecutor中进行创建,他装饰在BaseExector外面,就能被提前执行了。为了实现跨会话调用缓存,而BaseExector创建在会话之后,因此要跨会话,必须在BaseExector之前创建。

           使用:用户调用接口首先到二级缓存获取查询结果,然后再到BaseExector查询一级缓存是否能获取结果;

          适用场景:同一个namespace *享,跨会话查询;少量的增删改操作(会清除缓存);其他mappering即namespace对这个namespace进行了操作,会对缓存进行清空。

使用第三方缓存:

         全局配置中  <cache  type:"">   修改缓存类型为第三方缓存;  

      cursor:游标     

      datasource 数据源,使用的工厂方法模式,DataSourceFactory下面有DbcpDataSourceFactory、JndiDataSourceFactory、SimpleDataSourceFactory;     

      executor:执行器,使用包装器模式      io:IO封装      javassist:      jdbc      lang      logging:日志实现     

      mapping:Mapper.xml中对应的三大天王Statement、result、parameter

      ognl      parsing:解析      plugin:插件List存储      reflection:反射      scripting      session:会话      transaction:事务管理      type:字符类型Handler     

     

3、架构图

         看完了jar包之后,我们再看看其架构图

Mybatis体系结构与工作原理

        接口层:

接口层的核心对象是SqlSession,他是上层应用和MyBatis打交道的桥梁。

SqlSession上定义了很多对数据库操作的方法,

接口层在接收到调用请求的时候,会调用核心处理层的相应模块来完成具体的数据库操作。

       核心处理层: 处理数据库操作

配置解析:主要解析配置文件为我们的java操作对象,包括配置,statement,parament,Result;
参数处理:按照配置解析的对象进行属性的映射,参数的映射和动态SQL的生成;
sql执行:执行SQL语句,三种基本实现;
结果映射:三种方式进行处理结果集,并映射成Java对象;
插件:插件也属于核心处理层,因为他的工作方式和拦截的对象都在核心层;

      基础支持层:基础通用层

主要功能: 是由一些抽取出来的通用功能,用来支持核心处理层的功能。

包      括:数据源/连接池、缓存、日志、事务、反射等这些功能。

 

4,核心层代码深度解析

        1、获取resource,读取配置文件

        2、配置解析过程

1、解析了什么文件?

      XmlConfigBuilder解析全局配置文件,根标签<configuration>,根标签里面是所有一级标签的解析(解析字标签及属性)然后把解析内容存放在Configuration对象对应的属性中;     

      XmlMapperBuilder解析Mapper配置文件;

     XmlStatementBuilder解析 <select><update><insert><delete>标签中内容;

     XmlMapperEntityBuilder 解析对象参数;

2、怎么解析的?产生了什么对象?结果存放在哪里?

     XmlConfigBuilder解析一级标签:

            properties:解析子标签和属性,然后设置值到Configuration中的属性Properties中;

            settings:将其关键属性设置给Configuration,在不存在的时候设置默认值;在加载远程配置的时候需要其配置处理,因此先进行配置加载,后进行处理;

            typeAliases: 类型别名,存放在TypeAliasRegistry中

            plugins:插件解析,存放在InterceptorChain中

            objectFactory:创建实体类对象

            objectWrapperFactory:创建实体类包装对象

            reflectorFactory:反射工厂

            environment:产生事务工厂,数据源工厂,并通过数据源工厂产生一个数据源,

            databaseIdProvider:支持对数据的场景;

            typeHandlers:java类型和jdbc类型的相互转换,存放在TypeHandlerRegistry中;

            mappers:mapper映射路径去加载Mapper;     四种映射路径配置方式:package、resource、url、class;                             解析方式:①XmlMapperBuilder类创建调用parse()方法解析,对基本配置进行解析之后创建XmlStatementBuilder对增删改查sql进行解析,         

                                   ②然后调用MapperBuilderAssistant#addMappedStatement()方法产生了一个MappedStatement,                                     ③解析完成之后XmlMapperBuilder调用bindMapperFprNameSpace()将Mapper与命名空间绑定,将Mapper存放在MapperRegistry的属性knownMappers中,将命令空间接口和MapperProxyFactory工厂进行绑定

          3、会话创建过程:build方法创建SqlSession,然后openSession创建一个会话,会话中传入命名空间获取一个Mapper;

1,基本实现类:DefaultSqlSession创建,调用方法openSessionFormDataSource;

       首先,获取environment对象,用事务工厂创建事务;有两种模式:JDBC和Manager两种方式,Manager为容器管理

       然后,按照指定类型,创建Executor对象;   类型:BatchExecutor:支持Stament对象重用和批量语句执行,对比update方法     ReuseExecutor:支持Stament对象的重用,执行完之后会缓存起来,下次用;     SimpleExecutor:普通执行;                 

         设计模式:模板方法:BaseExecutor实现模板类,具体的增删改查都交给三大子类实现,     

                         包装器模式:创建二级缓存,用CacheExecutor包装BaseExecutor【new CachingExecutor(executor)】;

                         责任链模式:interceptorChain.pluginAll(executor)  将插件遍历出来进行链式包装executor。

       然后创建DefaultSqlSession,包含Executor、Configuration返回结果;

            4,获得Mapper对象

1,为什么要引入Mapper对象?

       解决以前调用Mapper传入接口号的硬编码问题;无法知道该接口是否存在,无法知道该接口被那些地方调用,调用了多少次。

2,Mapper对象是什么对象?

       是由MapperProxyFactory使用new MapperProxy(sqlSession, mapperInterface, methodCache)  创建;内部用jdk动态代理实现

       【疑问?】Mybatis的动态代码没有接口实现类的实例?为何不需要实现类?

         只要确保在接口对应的方法  能在Mapper中找到对应的id就可以了,不用一定要有实现类;调用任何都会跑到MapperProxy的invoke方法去,调用MapperMethod#execute() 获取DefaultSqlSession();    查询的话根据返回结果类型:游标,null,嵌套对象,Map调用对应的查询回来方法;

3,为什么要从SqlSession里面去获取?

       sqlSession代表一次会话,一次会话包含了整个sql执行的过程,且创建过程所需要的configurations在SqlSession中;

4,为什么传进去一个接口,然后还要用接口类型来接收?

      传进去接口是为了在knownMappers中用key去查找到对应的Mapper工厂,用接口类型来接收其一是方便对接口进行包装,其二是回传的并不是具体的实现,具体的实现还需要其调用对应的invoke方法去实现具体的调用。

          5、执行sql  ,实现类DefaultSqlSession # selectList()

1,接口方法是怎么找到要执行的SQL的?

      接口类型+方法名构成的字符串statement去获取的;

2,方法参数是怎么转换成Sql参数的?

      StatementHandler,根据类型创建不同的StatementHandler,  默认创建PreparedStatementHandler{

            实现基本属性赋值:configuration、executor、mappedStatement、rowBounds、boundSql、typeHandlerRegistry、objectFactory;    

            创建对象:parameterHandlerresultSetHandler;入参处理器,结果处理器;

      }

      设计模式:

             拦截器,可以对Executor、 StatementHandler、parameterHandlerresultSetHandler实现拦截;实现拦截调用interceptorChain.pluginAll( handler)  实现;

3,结果集怎么转换成对象?

        DefaultObjectFactory实现,

4,缓存的key产生机制,二级缓存在配置Mapper的时候根据配置创建二级缓存装饰BaseExecutor;

      CachingExecutor#createCacheKey()    mapper+id+翻页信息(开始偏移量+ 条数) +sql +参数信息 

 

5,核心对象总结

核心对象详解文件参考https://www.cnblogs.com/zsg88/category/1080098.html

Configuration:配置

DefaultSqlSession:会话

Executor:执行器

StatementHandler:jdbc的封装

ParameterHandler:入参数处理,进入sql

ResultSetHandler:结果处理

MapperProxy:实现invoke

MappedStatement:对  增删改查节点的封装

 

6,设计模式

工厂   SqlSessionFactory

单例   SqlSessionFactory  Configuration

建造者  SqlSessionFactoryBuilder

装饰者  CachingExecutor   batch  simple   resue;   缓存

代理    SqlSessionInterceptor   MapperProxy      Plugin     延迟加载实现-ProxyFactory

           Log:ConnectionLogger 、StatemLogger实现对执行器的代理

           PooledConnection

模板   : Executor    BaseExecutor  batch  simple   resue  

适配器: log4j是具体的日志的实现,      sl4j 是抽象的接口;   在应用中使用sl4j就可以轻易的切换;用具体的log4j很难切换,在需要自定义实现就适配实现

责任链  InterceptorChain    

策略模式:Handler 的实现