java-Mybatis源碼進階系列(一)

準備工作:github下載mybatis源碼 IDEA 2017.3.5;
本文通過單步調試AutoConstructorTest.java開始閱讀源碼
1 AutoConstructorTest.java如圖:

package org.apache.ibatis.autoconstructor;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.jdbc.ScriptRunner;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.Reader;
import java.sql.Connection;
import java.util.List;

public class AutoConstructorTest {
  private static SqlSessionFactory sqlSessionFactory;

  @BeforeClass
  public static void setUp() throws Exception {
    // create a SqlSessionFactory
    //獲取mybatis-config.xml的輸入流
    final Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml");
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    reader.close();
    //獲取session
    // populate in-memory database
    final SqlSession session = sqlSessionFactory.openSession();
    //獲取session連接
    final Connection conn = session.getConnection();
    //動態獲取絕對路徑下的sql腳本輸入流
    final Reader dbReader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/CreateDB.sql");
    //通過ScriptRunner運行sql腳本,創建數據庫表
    final ScriptRunner runner = new ScriptRunner(conn);
    runner.setLogWriter(null);
    runner.runScript(dbReader);
    conn.close();
    dbReader.close();
    session.close();
  }

  @Test
  public void fullyPopulatedSubject() {
    final SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
      final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
      final Object subject = mapper.getSubject(1);
      Assert.assertNotNull(subject);
    } finally {
      sqlSession.close();
    }
  }

  @Test(expected = PersistenceException.class)
  public void primitiveSubjects() {
    final SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
      final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
      mapper.getSubjects();
    } finally {
      sqlSession.close();
    }
  }

  @Test
  public void wrapperSubject() {
    final SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
      final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
      verifySubjects(mapper.getWrapperSubjects());
    } finally {
      sqlSession.close();
    }
  }

  @Test
  public void annotatedSubject() {
    final SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
      final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
      verifySubjects(mapper.getAnnotatedSubjects());
    } finally {
      sqlSession.close();
    }
  }

  @Test(expected = PersistenceException.class)
  public void badSubject() {
    final SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
      final AutoConstructorMapper mapper = sqlSession.getMapper(AutoConstructorMapper.class);
      mapper.getBadSubjects();
    } finally {
      sqlSession.close();
    }
  }

  private void verifySubjects(final List<?> subjects) {
    Assert.assertNotNull(subjects);
    Assertions.assertThat(subjects.size()).isEqualTo(3);
  }
}
 @BeforeClass
  public static void setUp() throws Exception {
    // create a SqlSessionFactory
    //1.1 獲取絕對路徑下面的mybatis-config配置文件
    final Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml");
    //1.2 獲取sqlSessionFactory
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);  
    // populate in-memory database
    final SqlSession session = sqlSessionFactory.openSession();
    final Connection conn = session.getConnection();
    final Reader dbReader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/CreateDB.sql");
    final ScriptRunner runner = new ScriptRunner(conn);
    runner.setLogWriter(null);
    runner.runScript(dbReader);
    conn.close();
    dbReader.close();
    session.close();
  }

SqlSessionFactoryBuilder獲取SqlSessionFactory:

這裏寫圖片描述

build方法:

//reader等於上面的reader, environment,Properties爲null
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      //1.3 這裏parser獲取configuration,重點解析怎樣獲取configuration
      //然後根據build方法獲取SqlSessionFactory
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

這裏寫圖片描述

1.4 parse.parse():

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    //解析mybatis-config.xml文件的configuration根節點
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      //解析並設置properties
      propertiesElement(root.evalNode("properties"));
      //解析並獲取setttings
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      //解析並獲取typeAliases
      typeAliasesElement(root.evalNode("typeAliases"));
      //解析並獲取plugins
      pluginElement(root.evalNode("plugins"));
      ////解析並獲取objectFactory
      objectFactoryElement(root.evalNode("objectFactory"));
      //解析並獲取objectWrapperFactory
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //解析並獲取reflectorFactory
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      //settingsElement 設置默認的Configuration靜態屬性如設置是否使用緩存等
    //configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      //解析environments節點並配置數據源
      environmentsElement(root.evalNode("environments"));
      //解析environments節點並配置數據源提供者(即mysql,oracle)
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //解析typeHandlers節點並配置類型解析器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析mappers節點並配置mappers
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

解析enviromentsElement配置數據源代碼如下

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          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());
        }
      }
    }
  }

至此,sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); 獲取sqlSessionFactory完畢!

2 2 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>

    <!-- autoMappingBehavior should be set in each test case -->

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC">
                <property name="" value=""/>
            </transactionManager>
            <dataSource type="UNPOOLED">
                <property name="driver" value="org.hsqldb.jdbcDriver"/>
                <property name="url" value="jdbc:hsqldb:mem:automapping"/>
                <property name="username" value="sa"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="org/apache/ibatis/autoconstructor/AutoConstructorMapper.xml"/>
    </mappers>

</configuration>

3 openSession獲取session解析:

這裏寫圖片描述

至此,獲取Session完畢!

4 獲取Connection
DefaultSqlSession.getConnection()
//獲取session連接
final Connection conn = session.getConnection();

  @Override
  public Connection getConnection() {
    try {
      return executor.getTransaction().getConnection();
    } catch (SQLException e) {
      throw ExceptionFactory.wrapException("Error getting a new connection.  Cause: " + e, e);
    }
  }

4.1 session.getConnection():
4.2 JdbcTransaction.getConnection():

 @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

openConnection:

 protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }

然後,調用 dataSource.getConnection();
然後,調用 UnpooledDataSource.doGetConenction:
UnpooledDataSource:
每個數據庫建立一個session連接:

public class UnpooledDataSource implements DataSource {

  private ClassLoader driverClassLoader;
  private Properties driverProperties;
  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();

  private String driver;
  private String url;
  private String username;
  private String password;

  private Boolean autoCommit;
  private Integer defaultTransactionIsolationLevel;
    ......
}

然後,調用doGetConnection

private Connection doGetConnection(String username, String password) throws SQLException {
    Properties props = new Properties();
    if (driverProperties != null) {
      props.putAll(driverProperties);
    }
    if (username != null) {
      props.setProperty("user", username);
    }
    if (password != null) {
      props.setProperty("password", password);
    }
    return doGetConnection(props);
  }

doGetConnection:

private Connection doGetConnection(Properties properties) throws SQLException {
//初始化驅動
    initializeDriver();
    //獲取connection
    Connection connection = DriverManager.getConnection(url, properties);
    configureConnection(connection);
    return connection;
  }

然後,調用initializerDriver:

 private synchronized void initializeDriver() throws SQLException {
 //根據CurrentHashMap類型的registeredDrivers判斷是否已註冊當前驅動,如果沒有,進行註冊,如果已經註冊,則跳出此函數
 //private static Map<String, Driver> registeredDrivers = new //ConcurrentHashMap<String, Driver>();
    if (!registeredDrivers.containsKey(driver)) {
      Class<?> driverType;
      try {
        if (driverClassLoader != null) {
        //根據類加載器獲取驅動類型 Class<?>
          driverType = Class.forName(driver, true, driverClassLoader);
        } else {
          driverType = Resources.classForName(driver);
        }
        // DriverManager requires the driver to be loaded via the system ClassLoader.
        // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
        //創建驅動類實例
        Driver driverInstance = (Driver)driverType.newInstance();
        //創建驅動代理對象並註冊驅動
        DriverManager.registerDriver(new DriverProxy(driverInstance));
        registeredDrivers.put(driver, driverInstance);
      } catch (Exception e) {
        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
      }
    }
  }

至此,初始化驅動完畢!
接着獲取connection並配置connection返回
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
至此,已經獲取到connection連接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章