美文网首页
Mybatis源码系列—初始化过程

Mybatis源码系列—初始化过程

作者: 不会飞的火腿肠256 | 来源:发表于2019-08-24 19:31 被阅读0次

代码片段

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>
    <!--定义数据库信息, 默认使用development数据库构建环境-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!--配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value=""/>
                <property name="username" value=""/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <!--定义映射器-->
    <mappers>
        <mapper resource="mapper/UserMapping.xml"/>
    </mappers>

</configuration>

User

package com.lalala.lightning.model;

import lombok.Data;
import javax.annotation.sql.DataSourceDefinition;


@Data
public class User {
    private String name;
    private Integer age;


}

UserDao


package com.lalala.lightning.dao;

import com.lalala.lightning.model.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;


@Mapper
public interface UserDao {
    List<User> selectByName(String name);
}

UserMapping.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lalala.lightning.dao.UserDao">

    <resultMap id="BaseResultMap" type="com.lalala.lightning.model.User">
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="age" jdbcType="VARCHAR" property="age"/>
    </resultMap>

    <select id="selectByName" resultType="com.lalala.lightning.model.User">
        select * from user where name = #{name}
    </select>

</mapper>

主方法

//定义SqlSessionFactory
SqlSessionFactory sqlSessionFactory = null;
//定义SqlSession
SqlSession sqlSession = null;
try {
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    //打开session会话
    sqlSession = sqlSessionFactory.openSession();
    //获取映射器mapper
    UserDao userDao = sqlSession.getMapper(UserDao.class);
    List<User> users = userDao.selectByName("小黑");
    System.out.println(users);
    //事务提交
    sqlSession.commit();
} catch (Exception e) {
    e.printStackTrace();
    //事务回滚
    assert sqlSession != null;
    sqlSession.rollback();

} finally {
    //关闭sqlSession,将连接归还给数据库,防止资源耗尽
    if (sqlSession != null) {
        sqlSession.close();
    }
}

源码解读

读取配置文件

第一步通过方法获取文件流,为后续读取配置做准备

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactoryBuilder

[图片上传失败...(image-f9c187-1566646246760)]

SqlSessionFactoryBuilder是用来创建SqlSessionFactory,通过上图可以看出SqlSessionFactoryBuilder通过build方法传入配置信息可以创建SqlSessionFactory。

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.
    }
  }
}

在SqlSessionFactoryBuilder中所有的build方法最终都会调用这个方法去构建SqlSessionFactory。

SqlSessionFactoryBuilder在build方法中传入配置信息创建一个XMLConfigBuilder对象。接下来 我们看下XMLConfigBuilder的体系结构。

BaseBuilder.png

不同的Builder类用来解析不同的XMl文件或者针对XMl中的特定信息进行解析。

  • XMLConfigBuilder:解析mybatis-config.xml文件。
  • XMLMapperBuilder:解析Mapping.xml文件。
  • XMLStatementBuilder:解析文件中的SQL语句。
  • SqlSourceBuilder:替换SQL语句中的占位符。
  • MapperBuilderAssistant:用于缓存、sql参数、查询返回的结果集处理。
  • XMLScriptBuilder:真正负责在背后解析mapper文件中的每个节点。

接下来我们从XMLConfigBuilder的构造方法开始分析,从这里开始读取配置信息。

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
  super(new Configuration());
  ErrorContext.instance().resource("SQL Mapper Configuration");
  this.configuration.setVariables(props);
  this.parsed = false;
  this.environment = environment;
  this.parser = parser;
}

首先创建一个Configuration在对象,在Configuration的构造方法中注册一些Class对象。

public Configuration() {
  typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

  typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

  typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
  typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
  typeAliasRegistry.registerAlias("LRU", LruCache.class);
  typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
  typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

  typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

  typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
  typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

  typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
  typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
  typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
  typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
  typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
  typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
  typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

  typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
  typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

  languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  languageRegistry.register(RawLanguageDriver.class);
}

在创建好XMLConfigBuilder对象之后,调用了parse方法。

private void parseConfiguration(XNode root) {
  try {
    //issue #117 read properties first
    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);
  }
}

在parseConfiguration方法中,开始解析xml中节点的信息。最终这些配置信息都会存储在Configuration对象中维护。通过读取配置,XMLConfigBuilder的parse方法会返回一个携带配置的Configuration。

到此为止整个mybatis-config.xml配置信息都已经全部获取到了。

接下来开始创建SqlSessionFactory。

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

在DefaultSqlSessionFactory方法中比较简单,只是把具体配置信息的Configuration对象赋予DefaultSqlSessionFactory中。

public DefaultSqlSessionFactory(Configuration configuration) {
  this.configuration = configuration;
}

整个Mybatis的初始化过程到这里就全部结束了。

相关文章

网友评论

      本文标题:Mybatis源码系列—初始化过程

      本文链接:https://www.haomeiwen.com/subject/oogqectx.html