关于mybatis:mybatis框架上主流程分析

58次阅读

共计 8844 个字符,预计需要花费 23 分钟才能阅读完成。

// # 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 文件自身。

<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/BlogMapper.xml"/>
    </mappers>
</configuration>

能够看到,最外层是一个 <configuration> 标签,外面可退出很多配置,如:

  • 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<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    return mapperProxyFactory.newInstance(sqlSession);
    
    org.apache.ibatis.binding.MapperProxyFactory
        #newInstance(org.apache.ibatis.binding.MapperProxy<T>)
    {
        // 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 的一、二级缓存。

这部分内容放在下篇文章剖析。

附录

P6-P7 常识合辑

正文完
 0