Mybatis源码日记(一)

Mybatis执行对数据库的操作主要通过Sqlsession完成的,Sqlsession是个接口,它有两个实现类,DefaultSqlSession和SqlSessionManager。

  • DefaultSqlSession:Mybatis在构建的时候,默认创建了DefaultSqlSessionFactory,DefaultSqlSessionFactory的openSesstion()方法返回DefaultSqlSession也就是说Mybatis默认的SqlSession实现通常使用的就是这个,非线程安全。

  • SqlSessionManager:实现了SqlSession,SqlSessionFactory,这个可以开启线程安全的Sqlsession,通过startManagedSession()方法开启线程安全的SqlSession。也可以直接使用sqlSessionProxy对象执行sql语句。通过startManagedSession()开启线程安全后,切记不能openSession(),或者startManagedSession()再次获取新的SqlSession,而是通过getConnection()获取数据库连接来执行Sql。

SqlSessionManager有三个成员变量

// 用来创建Sqlsession
private final SqlSessionFactory sqlSessionFactory;
// SqlSession的实现通过这个动态代理session实例执行
private final SqlSession sqlSessionProxy;
// 先通过ThreadLocal<SqlSession> localSqlSession获取sqlsession,如果获取不到则通过:openSession新开一个session,如果存在就使用。
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<>();

一般情况下,我们通过以下方法可以获取到Sqlsession对象

Reader resourceAsStream = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();

在调用SqlSessionFactoryBuilder的build()方法时,通过源码可以发现,build方法被重载了9次。

org.apache.ibatis.session.SqlSessionFactoryBuilder:

public SqlSessionFactory build(Reader reader);
public SqlSessionFactory build(Reader reader, String environment);
public SqlSessionFactory build(Reader reader, Properties properties);
// 上面三种方法其实最后都会调用这个方法
public SqlSessionFactory build(Reader reader, String environment, Properties properties);

public SqlSessionFactory build(InputStream inputStream);
public SqlSessionFactory build(InputStream inputStream, String environment);
public SqlSessionFactory build(InputStream inputStream, Properties properties);
// 上面这三种方法最后都会调用这个方法,和第4行方法的区别是,这里接收的是一个字节流,而第4行接收的是一个字符流
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties);

// 这个方法最终都会被第4个和第8个方法通过build(parser.parse())方法调用
public SqlSessionFactory build(Configuration config) {
    // 这里返回了DefaultSqlSessionFactory
    return new DefaultSqlSessionFactory(config);
}

第4行和第8行的具体实现其实都是一样的,只是接收的流不一样,一个是字节流,一个是字符流,以第8行方法实现为例:

org.apache.ibatis.session.SqlSessionFactoryBuilder:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        // 解析mybatis-config.xml配置文件
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        // 调用第9行的build方法返回DefaultSqlSessionFactory对象,通过DefaultSqlSessionFactory的openSession()方法可以返回DefaultSqlSession对象
        return build(parser.parse());
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
            inputStream.close();
        } catch (IOException e) {
            // Intentionally ignore. Prefer previous error.
        }
    }
}

先来简单看下XMLConfigBuilder,可以看到XMLConfigBuilder和SqlSessionFactoryBuilder有些类似,XMLConfigBuilder的构造方法被重载了7次。

public XMLConfigBuilder(Reader reader);
public XMLConfigBuilder(Reader reader, String environment);
// 上面两个构造方法也是调用这个,用的是字符流
public XMLConfigBuilder(Reader reader, String environment, Properties props);

public XMLConfigBuilder(InputStream inputStream);
public XMLConfigBuilder(InputStream inputStream, String environment);
// 上面两个构造方法也是调用这个,用的是字节流
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props);

// 第3行和第6行方法都是调用的这个方法。这个方法被私有化了,应该是作者不允许有人从外部直接调用
private XMLConfigBuilder(XPathParser parser, String environment, Properties props);

第7行XMLConfigBuilder构造方法实现

org.apache.ibatis.builder.xml.XMLConfigBuilder

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    // parsed标识是否被解析
    this.parsed = false;
    this.environment = environment;
    // xpath解析器
    this.parser = parser;
}

回到SqlSessionFactoryBuilder,接下来是parser.parse()实现,这个方法主要是对mybatis-config.xml配置文件做了解析

org.apache.ibatis.builder.xml.XMLConfigBuilder:

public Configuration parse() {
    // 判断是否已经被解析过了
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    // 接下来要开始解析了,所以提前将它标识为已解析,避免后续调用parse时又被解析一遍
    parsed = true;
    // mybatis-config.xml根节点就是configuration
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}

mybatis-config.xml配置文件结构:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties></properties>
    <settings>
        <setting name="" value=""/>
    </settings>
    <typeAliases></typeAliases>
    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>
    <objectFactory type=""></objectFactory>
    <objectWrapperFactory type=""></objectWrapperFactory>
    <reflectorFactory type=""></reflectorFactory>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value=""/>
                <property name="url" value=""/>
                <property name="username" value=""/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <databaseIdProvider type=""></databaseIdProvider>
    <typeHandlers></typeHandlers>
    <mappers>
        <!-- <mapper resource=""/> -->
        <!-- mapper和package二选一 -->
        <package name=""/>
    </mappers>
</configuration>

最后看下parseConfiguration()方法做了哪些事,parseConfiguration()将mybatis-config.xml配置所有的子节点一一解析。

org.apache.ibatis.builder.xml.XMLConfigBuilder:

private void parseConfiguration(XNode root) {
    try {
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // read it after objectFactory and objectWrapperFactory issue #631
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

最后,parse()方法返回Configuration对象,在SqlSessionFactoryBuilder中build方法接收这个对象并调用,返回DefaultSqlSessionFactory对象。通过openSession()方法可以返回DefaultSqlSession,DefaultSqlSession继承自SqlSession。

参考:

https://mybatis.org/mybatis-3/zh/index.html

https://blog.csdn.net/u012746134/article/details/94658840

posted @ 2019-12-06 17:40  dagger9527  阅读(167)  评论(0编辑  收藏  举报