传统方式
- 将mybatis-config.xml加载入inputStream中。
- 调用sqlsessionFactotyBuilder().build()方法,将流传入其中。
- sqlsessionFactotyBuilder().build()方法中有一个XMLConfigBuilder类解析xml中的内容,封装为一个configuration实例,configuration对象的结构和xml配置文件的对象几乎相同。
- 且mybatis-config.xml中的mappers标签映射的mapper.xml或者配置mapper接口的目录,每一个查询sql标签都会被解析封装成一个MappedStatement对象,然后存储在 Configuration对象的mappedStatements属性中,mappedStatements是一个HashMap,存储时key = 全限定类名 + 方法名, value = 对应的 MappedStatement对象。
- 到此对xml配置文件的解析就结束了,回到build()方法返回一个sqlSessionFactory对象,并将configuration对象传入其中。
- 再进入sqlSessionFactory.openSession()方法,在内部为configuration创建指定类型的Executor,返回一个DefaultSqlsession(configuration, executor,//事务隔离级别autoCommit);
- 执行sqlSession中的api,方法内部实现是根据传入的全限定类名+方法名从映射的map中取出MapperStatement对象,调用Executor执行器进行处理,例如executor.query(),根据MapperStatement对象中的parameterType参数动态获取到SQL语句,最后返回BoundSql对象表示,再为本次查询创建缓存的key。
- 根据key值从缓存中查询,若缓存中没有就从数据库中查询,查询出来后把结果放入缓存,若缓存中有则直接从缓存中获取。
- 从数据库查询的过程:根据传入参数创建JDBC的Statement连接对象,再将所有必要参数传给StatementHandler来完成对数据库的查询,最终返回结果集。
- 这里的StatementHandler主要完成两个工作,一是对JDBC的Statement对象里的占位符进行设值,二是执行数据库操作,并将Statement对象返回的结果集封装为具体的对象。
Mapper代理方式
通常Mapper接口我们什么方法都没有实现确可以使用,利用了动态代理的机制,
首先介绍一下MyBatis初始化时对接口的处理:MapperRegistry是Configuration中的一个属性,内部维护一个HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类。
- 前面的步骤都一样,有区别的地方在于,封装configuration实例时,mybatis-config.xml的mappers标签内配置的是接口的包路径,当解析到是接口时会创建此接口对应的MapperProxyFactory对象,存入HashMap中,Key = 接口的字节码对象,value = 此接口对应的MapperProxyFactory对象。
- sqlSession.getMapper(UserMapper.class)方法,根据传入的字节码对象从MapperRegistry中的HashMap中拿到MapperProxyFactory对象,如果该对象是为null的,就抛出异常,如果不为null就通过动态代理生成实例。
- 代理对象调用方法时,都要执行动态代理实例中的invoke()方法,内部是获取了一个mapperMethod对象,调用它的execute()方法。
- execute()方法根据mapper中的类型以及接口的返回值类型来判断具体调用SqlSession中的哪个方法。