mybatis源码分析-SqlSessionFactory构建过程

mybatis源码分析-环境搭建 一文中,咱们的测试代码以下:java

public static void main(String[] args) throws IOException { 
    String resource = "mybatis-config.xml";  
    InputStream inputStream = Resources.getResourceAsStream(resource);  
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  
    SqlSession sqlSession = sqlSessionFactory.openSession();  
    try {  
        DeptMapper deptMapper = sqlSession.getMapper(DeptMapper.class);  
        List<Dept> deptList = deptMapper.getAllDept();  
        System.out.println(deptList);  
    } finally {  
        sqlSession.close();  
    }  
 }

其中如何生成 InputStream 对象在 mybatis源码分析-配置文件加载 已经讲解。本次将探究 SqlSessionFactory 对象的生成,也就是下面这行代码执行了什么。sql

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

源码分析

首先上面那行代码,建立了一个 SqlSessionFactoryBuilder 对象,源码以下:apache

public class SqlSessionFactoryBuilder {  
    public SqlSessionFactoryBuilder() {  
    }
    //省略其它代码
}

其次 SqlSessionFactoryBuilder 对象调用 build 方法,该方法源码以下:segmentfault

public SqlSessionFactory build(InputStream inputStream) {  
    return this.build((InputStream)inputStream, (String)null, (Properties)null);  
}

这个方法看来有重载方法,咱们继续看其重载方法:session

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {  
  try {  
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);  
    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.  
  }  
  }  
}

这个方法主要关注:mybatis

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

这个构造函数将咱们的配置文件转换为 XMLConfigBuilder 对象,这里面的逻辑十分复杂,咱们暂且不深究。app

return build(parser.parse());

这里有两处注意的地方,parser.parse() 返回一个Configuration 对象,这个对象保罗万千,暂时也不深究。此外build方法的实现以下:ide

public SqlSessionFactory build(Configuration config) {  
    return new DefaultSqlSessionFactory(config);  
}

它返回了 DefaultSqlSessionFactory 对象,而且将 Configuration 对象赋值其属性,有源码为证:函数

public class DefaultSqlSessionFactory implements SqlSessionFactory {  
  
  private final Configuration configuration;  
  
  public DefaultSqlSessionFactory(Configuration configuration) {  
    this.configuration = configuration;  
  }
  //省略其它代码
}

因为 DefaultSqlSessionFactory 实现了 SqlSessionFactory 接口,那么源码分析

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

就水到渠成了。


本节不想讨论

XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

return build(parser.parse());

这两行代码的实现,由于它们太复杂了。目前只须要理解:

  • 第一行经过配置文件建立了一个 XMLConfigBuilder 对象
  • 第二行经过 XMLConfigBuilder 对象的一个 parse 方法将 XMLConfigBuilder 对象转换为 Configuration 对象,而该对象是 DefaultSqlSessionFactory 必需要的属性。只有经过这个属性,才能实现 SqlSessionFactory 中定义的接口方法。

源码设计思想

若是咱们来实现上面的功能,通常人会怎么处理呢?

  • 首先定义接口
package com.yefengyu.mybatis;  
  
public interface SqlSessionFactory {  
    void test();  
}
  • 其次编写实现类
package com.yefengyu.mybatis;  
  
import org.apache.ibatis.builder.xml.XMLConfigBuilder;  
import org.apache.ibatis.session.Configuration;  
  
import java.io.InputStream;  
  
public class DefaultSqlSessionFactory implements SqlSessionFactory {  
  
    private Configuration configuration = null;  
  
    public DefaultSqlSessionFactory(InputStream inputStream) {  
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream);  
        this.configuration = parser.parse();  
    }  
  
    @Override  
    public void test() {  
        //使用 configuration 完成相关功能  
    }  
}
  • 测试
package com.yefengyu.mybatis;  
  
import org.apache.ibatis.io.Resources;  
  
import java.io.IOException;  
import java.io.InputStream;  
  
public class Main {  
    public static void main(String[] args) throws IOException {  
        String resource = "mybatis-config.xml";  
        InputStream inputStream = Resources.getResourceAsStream(resource);  
        SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(inputStream);  
        sqlSessionFactory.test();  
    }  
  
}

这三段代码想模拟mybatis建立SqlSessionFactory对象的过程,好比SqlSessionFactory有一些接口 test,其实现类 DefaultSqlSessionFactory 实现此方法须要 Configuration 对象,须要从 InputStream 经过构造函数传入并解析为 Configuration对象。测试代码中直接使用 DefaultSqlSessionFactory 建立 SqlSessionFactory 对象。

这种方式的缺点

  • 首先若是有多个 SqlSessionFactory 实现的话,把 InputStream 转换为 Configuration的过程在每一个构造函数都会有。
  • 其次客户端,也就是测试代码那块,必需要清楚 SqlSessionFactory 有哪些实现类,每一个实现类的功能是什么,没有达到接口与实现的分离。
相关文章
相关标签/搜索