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 有哪些實現類,每一個實現類的功能是什麼,沒有達到接口與實現的分離。
相關文章
相關標籤/搜索