原创-MyBatis工作原理

一、MyBatis的架构

接口层:和用户直接交互
数据处理层:原生JDBC的流程
框架支撑层:支持数据处理层走完整个流程
引导层:引导启动mybatis,基于XML配置文件的方式/JavaAPI方式创建SqlSessionFactory

二、MyBatis流程

1.根据全局配置文件创建SqlSessionFactory对象
2.获取SqlSession对象
3.获取接口的代理对象(MapperProxy)
4.执行代理对象的增删改查方法
插件能拦截四大对象的每一个方法,看源码为了能理解插件的工作原理,拦截如下四大对象
Executor 执行器
ParameterHandler 参数处理器
ResultSetHandler 结果集处理器
StatementHandler SQL语句的处理器
SQLSessionFactory是接口,实际起作用的是DefaultSqlSessionFactory对象
SqlSession是接口,实际起作用的是DefaultSqlSession对象
Executor是接口,实际起作用的是SimpleExecutor/ReuseExecutor/BatchExecutor,规范了增删改用的update以及query所有重载方法
StatementHandler是接口,实际起作用的是PreparedStatementHandler/SimpleStatementHandler
ParameterHandler是接口,实际起作用的是DefaultParameterHandler
ResultSetHandler是接口,实际起作用的是DefaultResultSetHandler

三、获取SQLSessionFactory对象的底层实现

总结:解析文件的每一个信息保存在Configuration对象中,返回包含Configuration的DefaultSqlSessionFactory对象。

关于Configuration对象
1.Configuration对象保存了所有配置文件的详细信息,包括全局配置文件和SQL映射文件
2.Configuration对象保存了所有增删改查标签的详细信息,key为mappedStatements,value为Map集合,Map集合的key为每个标签的id,value为封装当前标签信息的mappedStatement对象,一个MappedStatement对象代表一个增删改查标签的详细信息
3.Configuration对象的mapperRegistry里面保存了knownMapper,保存了每一个接口对应的MapperProxyFactory(用于创建MapperProxy对象的工厂,该对象在这个阶段就已经被创建好了,一堆这样的对象存在map里,等待着在获取代理对象阶段通过接口的类型被获取到)

 四、获取SqlSession对象的底层实现

解释:底层调用的是openSessionFromDataSource,先从configuration拿到默认的executor类型(有三种类型:simple简单的执行器/reuse可复用的执行器/batch可批量操作的执行器),执行openSessionFromDataSource方法,从configuration中获取一些信息,创建了一个事务,用configuration对象根据事务和执行器的类型创建执行器executor,根据执行器在全局配置的类型创建不同的执行器对象,判断是否配置了二级缓存,如果有二级缓存用CachingExecutor包装(装饰者模式),所有的增删改查底层还是用的传进来的executor实现类调用的,只不过在查询方法之前先去查询缓存是否有相同的key(SQL语句),包装好了赋值,再使用每一个拦截器重新包装executor,最终返回完成包装的executor,创建DefaultSqlSession对象,传入包装好的executor对象和configuration对象,传入的是executor但最终工作的是DefaultSqlSession对象,而DefaultSqlSession对象就是SqlSession接口的实现类。
总结:返回一个DefaultSqlSession对象。包含Executor(执行增删改查时用它)和Configuration(获取代理对象时用它);Executor(经过三层包装终于见面了!)对象会在这一步被创建

五、获取接口的代理对象的底层实现

最终返回的代理对象:
getMapper方法调用的是configuration对象底层的
①mapperRegistry对象的getMapper方法(保存了每一个mapper对应的MapperProxyFactory对象)
②方法里获取到MapperProxyFactory(通过传入的接口类型,根据键获取值)
③调用MapperProxyFactory对象的newInstance方法底层通过动态代理技术返回MapperProxy的代理对象,方法里面第一步先根据传入的DefaultSqlSession对象创建 MapperProxy对象(MapperProxy就是InvocationHandler的子类对象,包含DefaultSqlSession对象,当前接口的Class对象,接口每一个方法的映射),方法里第二步MapperProxy传入动态代理的方法创建代理对象,将代理对象一层一层的返回给调用者
总结:getMapper时,根据接口的类型获取对应的MapperProxyFactory,用动态代理技术创建MapperProxy的代理对象并返回,代理对象包含了DefaultSqlSession(Executor)

六、代理对象执行查询方法的底层实现

第一步先执行mapperProxy对象的invoke方法
---->在invoke方法里面第一步,把当前方法包装成一个MapperMethod对象(Mybatis能认识的,装饰者模式)
---->在invoke方法里面第二步,调用MapperMethod对象的execute方法(传入:defaultSqlSession对象,当前方法的参数)
-------->在execute方法里面第一步,判断当前SQL语句的CRUD类型,来到对应的CRUD方法
-------->在execute方法里面第二步,method调用参数解析方法,把传进来的参数转换成能用的参数(如果参数是单个,直接返回,如果参数是多个,包装成一个map返回)
-------->在execute方法里面第三步,调用defaultSqlSession对象的selectOne方法查询单个[查询多个:executeForMany方法查询多个](传入:command对象获取sql语句的准确id[哪个接口的哪个方法,当前SQL语句的唯一标识],被封装的参数)
---------------->在selectOne方法里面,调用本类对象defaultSqlSession的selectList方法[如果是单个就拿到第一个值而已],拿到查询结果返回(传入:statement[就是当前SQL语句的唯一标识],被封装的参数)
-------------------------------->在selectList方法里面第一步,从configuration对象中根据statement获取当前SQL标签的详细信息MappedStatement对象
-------------------------------->在selectList方法里面第二步,调用executor对象[CachingExecutor]的query方法(传入:MappedStatement对象,参数)
------------------------------------------------>在query方法里面第一步,从MappedStatement对象获取(参数)绑定的SQL[包括sql语句,参数映射,参数值]
------------------------------------------------>在query方法里面第二步,创建缓存的key[不是创建缓存,是缓存的key而已,一二级都是这个key,缓存是一级关闭后保存到二级]
------------------------------------------------>在query方法里面第三步,调用本类重载的query方法进行查询,从MappedStatement对象获取二级缓存
------------------------------------------------>在query方法里面第四步,如果没有取到二级缓存,调用SimpleExecutor的query方法
---------------------------------------------------------------->在SimpleExecutor的query方法第一步,从本地缓存中根据缓存的key获取一级缓存
---------------------------------------------------------------->在SimpleExecutor的query方法第二步,如果没有取到一级缓存,调用本类的queryFromDatabase方法
------------------------------------------------------------------------>在SimpleExecutor的queryFromDatabase方法第一步,调用doQuery方法查出数据
-------------------------------------------------------------------------------->doQuery方法第一步,从MappedStatement对象获取当前的配置信息configuration
-------------------------------------------------------------------------------->doQuery方法第二步,调用Configuration对象的newStatementHandler方法创建StatementHandler对象
----------------------------------------------------------------------------------------->从MappedStatement获取StatementType[在sql标签可以设置原生的Statement或 默认->预编译的PreparedStatement]
----------------------------------------------------------------------------------------->根据设置的类型创建PreparedStatementHandler对象或其他(构造器里调用Configuration对象创建ParameterHandler对象,创建时也是用拦截器包装;调用Configuration对象创建ResultSetHandler对象,创建时也是用拦截器包装)
----------------------------------------------------------------------------------------->使用拦截器包装PreparedStatementHandler对象
-------------------------------------------------------------------------------->doQuery方法第三步,根据PreparedStatementHandler对象创建Statement对象
------------------------------------------------------------------------------------------>拿到一条连接
------------------------------------------------------------------------------------------>调用PreparedStatementHandler对象parameterize方法(Statement对象)进行参数预编译
------------------------------------------------------------------------------------------------>在parameterize方法里面,ParameterHandler对象[在创建PreparedStatementHandler时被创建的]调用setParameters设置参数(Statement对象)
------------------------------------------------------------------------------------------------------>在setParameters方法里面,获取typeHandler,调用类型处理器typeHandler的setParameter(PreparedStatement对象),给PreparedStatement对象设置参数
-------------------------------------------------------------------------------->doQuery方法第四步,调用PreparedStatementHandler对象的query方法(PreparedStatement对象)查出数据[装饰者模式,底层还是PreparedStatement对象的execute方法查出的数据]
------------------------------------------------------------------------------------------>用ResultSetHandler对象来封装处理查出的结果,使用typeHandler的getResult方法获取value值,把JavaBean里面每一个属性在SQL语句结果里面对应的值取出
------------------------------------------------------------------------>在SimpleExecutor的queryFromDatabase方法第二步,把数据保存[putObject]到本地缓存中(key,list)
总结:看起来Executor是执行增删改查的,但是它执行增删改查也是用StatementHandler,而StatementHandler执行增删改查在设置参数时用ParameterHandler,处理结果时用ResultSetHandler,整个参数设置与结果处理牵扯到类型转换要用TypeHandler

七、MyBatis底层实现总结

1、根据配置文件(全局,sql映射)初始化出Configuration对象
2、创建一个DefaultSqlSession对象,他里面包含Configuration以及Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;MapperProxy里面有(DefaultSqlSession);
4、执行增删改查方法:
1)、调用DefaultSqlSession的增删改查(Executor);
2)、会创建一个StatementHandler对象。(同时也会创建出ParameterHandler和ResultSetHandler)
3)、调用StatementHandler预编译参数以及设置参数值; 使用ParameterHandler来给sql设置参数
4)、调用StatementHandler的增删改查方法;
5)、ResultSetHandler封装结果
注意:四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);

 

 

 

 

 

 

 

 

 

 

 

posted @ 2022-03-11 00:48  独醉乄  阅读(138)  评论(0)    收藏  举报