mybatis框架(上)(主流程分析)

// # 1.读取配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // # 2.创建SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // # 3.通过sqlSessionFactory创建SqlSession try (SqlSession session = sqlSessionFactory.openSession()) { // # 4.通过SqlSession获取Mapper,并查询 BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlog(101); }

以上是Mybatis官网的demo,这次DB查询操作基本分成了上述4个步骤。
那么问题来了:在一次DB查询过程,mybatis框架具体做了什么?
直接给出结论:
1.读取mybatis-config.xml并解析,将全部配置信息和各种默认配置加载到Configuration中,包含:数据源信息(分装在Environment)、类型处理器、类型别名、mapper代理工厂等。
Configuration是SqlSessionFactory的重要属性。
2.通过SqlSessionFactory创建transaction、executor、sqlSession。
3.通过sqlSession获取mapper,本质是通过mapper代理工厂创建mapper代理。mapper的crud方法实际执行的是MapperProxy的相关方法。
在这些方法中,sql会交由executor执行,而executor最终会调用jdbc的statement。
接下来通过源码分析demo中的四步。
一、读取配置文件
// == A.mybatis配置文件转换成stream InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

配置文件读取部分没什么可说的,无非把File转换成IO流。
我们把目光聚焦于mybatis-config.xml文件本身。

可以看到,最外层是一个标签,里面可加入很多配置,如:
  • properties(属性)
  • settings(设置)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • environments(环境配置)
    • environment(环境变量)
      • transactionManager(事务管理器)
      • dataSource(数据源)
  • databaseIdProvider(数据库厂商标识)
  • mappers(映射器)
了解这些就可以了,后面几步才是重点。
二、创建SqlSessionFactory
// == B.初始化Configuration,并加载各类默认配置(主要是类型转换器和别名映射) SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream) org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties) public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { // ## 1.解析 mybatis-config.xml,同时创建 Configuration 对象 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); // ## 2.解析XML,最终返回一个 DefaultSqlSessionFactory return build(parser.parse()); }

1.解析 mybatis-config.xml
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { // 构造函数完成了Configuration的初始化,并将configuration的两个重要属性传递过来 super(new Configuration()); ????? this.configuration = configuration; this.typeAliasRegistry = this.configuration.getTypeAliasRegistry(); this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry(); ????? super(new Configuration()); }

观察传递过来的两个属性
### org.apache.ibatis.session.Configuration类 ###// 类型处理注册器 protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this); // 类型别名注册器 protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

分别查看它们的构造函数。
  • 类型处理注册器TypeHandlerRegistry
为特定类型创建处理器
org.apache.ibatis.type.TypeHandlerRegistry#TypeHandlerRegistry{ register(Boolean.class, new BooleanTypeHandler()); register(JdbcType.BIT, new BooleanTypeHandler()); register(Byte.class, new ByteTypeHandler()); register(JdbcType.TINYINT, new ByteTypeHandler()); register(Short.class, new ShortTypeHandler()); register(JdbcType.SMALLINT, new ShortTypeHandler()); }

  • 类型别名注册器TypeAliasRegistry
为基本类型起别名
org.apache.ibatis.type.TypeAliasRegistry#TypeAliasRegistry{ registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); }

  • 再观察Configuration的构造函数
org.apache.ibatis.session.Configuration#Configuration() // 构造函数做各种别名设置 public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class); }

2.解析XML 这一步做了很多事,篇幅关系不能全部分析。
org.apache.ibatis.builder.xml.XMLConfigBuilder#parse org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration private void parseConfiguration(XNode root) { // 别名解析 typeAliasesElement(root.evalNode("typeAliases")); // 注册插件(过滤器,如PageHelper) pluginElement(root.evalNode("plugins")); // 对象工厂解析 objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); // 属性赋值(设置默认属性) settingsElement(settings); { configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); } // 初始化environment,设置事务处理工厂、数据源 environmentsElement(root.evalNode("environments")); { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 自定义类型转换器 typeHandlerElement(root.evalNode("typeHandlers")); // ### 解析mapper文件 mapperElement(root.evalNode("mappers")); }

重点看一下“解析mapper文件”部分:
org.apache.ibatis.session.Configuration#addMappers(java.lang.String) org.apache.ibatis.binding.MapperRegistry#addMappers(java.lang.String) org.apache.ibatis.binding.MapperRegistry#addMappers(java.lang.String, java.lang.Class) // == 存放Mapper(代理)工厂 org.apache.ibatis.binding.MapperRegistry#addMapper{ knownMappers.put(type, new MapperProxyFactory<>(type)); }

最终会将Mapper代理工厂存放在一个map集合中,把“mapper定义的接口类”作为key。
三、通过sqlSessionFactory创建SqlSession
// == D. 创建session,DefaultSqlSession SqlSession session = sqlSessionFactory.openSession() org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession() org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 创建Executor,默认SimpleExecutor final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); }

最终创建的是DefaultSqlSession,它封装了configuration和executor。
四、通过SqlSession获取Mapper,并查询
// == E. 获取mapper,通过MapperProxyFactory创建代理对象 BlogMapper mapper = session.getMapper(BlogMapper.class); org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper org.apache.ibatis.binding.MapperRegistry#getMapper { final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type); return mapperProxyFactory.newInstance(sqlSession); org.apache.ibatis.binding.MapperProxyFactory #newInstance(org.apache.ibatis.binding.MapperProxy) { // java方式创建代理对象 Proxy.newProxyInstance( mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } }

最终得到的mapper,其实是一个由MapperProxyFactory创建的代理;而代理类的InvocationHandler指向了MapperProxy。
那么我们直接查看MapperProxy的invoke方法就好了。
// == F. 方法执行,会进入InvocationHandler(MapperProxy)的invoke方法 Blog blog = mapper.selectBlog(101); org.apache.ibatis.binding.MapperProxy#invoke org.apache.ibatis.binding.MapperMethod#execute{ // == 按操作类型区分 case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult( ## update执行 sqlSession.update(command.getName(), param)); break; } case DELETE: ... case SELECT: ... }

我们以update为例,观察它的具体执行
update执行
org.apache.ibatis.session.defaults.DefaultSqlSession#update(java.lang.String, java.lang.Object) update(String statement, Object parameter) { MappedStatement ms = configuration.getMappedStatement(statement); // == 真正的执行动作在executor中 return executor.update(ms, wrapCollection(parameter)); }

org.apache.ibatis.executor.BaseExecutor#update // 查看默认的SimpleExecutor实现 org.apache.ibatis.executor.SimpleExecutor#doUpdate public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; Configuration configuration = ms.getConfiguration(); // 封装成StatementHandler StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); // 准备了Statement(已经是jdbc逻辑) stmt = prepareStatement(handler, ms.getStatementLog()); // == 最终执行 return handler.update(stmt); }// 最终执行我们依然查看SimpleStatementHandler,发现就是熟悉的jdbc statement执行sql逻辑 org.apache.ibatis.executor.statement.SimpleStatementHandler#update{ statement.execute(sql); }

select执行 文章开始时,我们的计划是分析一次select执行
其实select与update的执行过程差不多,最终都会靠jdbc的statement执行。最大的差异在于select可以用到缓存,也就是人们常说的mybatis的一、二级缓存。
这部分内容放在下篇文章分析。
附录 【mybatis框架(上)(主流程分析)】P6-P7知识合辑

    推荐阅读