本文共享自华为云社区《MyBatis详解 – 初始化根本进程》,作者:龙哥手记 。

MyBatis初始化的办法及引入

MyBatis的初始化能够有两种办法:

  • 依据XML装备文件:依据XML装备文件的办法是将MyBatis的一切装备信息放在XML文件中,MyBatis经过加载并XML装备文件,将装备文信息组装成内部的Configuration目标。

  • 依据Java API:这种办法不运用XML装备文件,需要MyBatis运用者在Java代码中,手动创立Configuration目标,然后将装备参数set 进入Configuration目标中。

初始化办法 – XML装备

接下来咱们将经过 依据XML装备文件办法的MyBatis初始化,深入探讨MyBatis是怎么经过装备文件构建Configuration目标,并运用它。

现在就从运用MyBatis的简略比如下手,深入分析一下MyBatis是怎样完结初始化的,都初始化了什么。看以下代码:

// mybatis初始化
String resource = "mybatis-config.xml";  
InputStream inputStream = Resources.getResourceAsStream(resource);  
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创立SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();  
// 履行SQL句子
List list = sqlSession.selectList("com.foo.bean.BlogMapper.queryAllBlogInfo");

有过MyBatis运用经验的读者会知道,上述句子的作用是履行com.foo.bean.BlogMapper.queryAllBlogInfo 界说的SQL句子,回来一个List成果集。总的来说,上述代码经历了三个阶段(本系列也对应三篇文章别离解说):

  • mybatis初始化 本文
  • 创立SqlSession – 详解后文
  • 履行SQL句子 – 详解后文

上述代码的功用是依据装备文件mybatis-config.xml 装备文件,创立SqlSessionFactory目标,然后发生SqlSession,履行SQL句子。而mybatis的初始化就发生在第三句:SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 现在就让咱们看看第三句究竟发生了什么。

MyBatis初始化根本进程:

SqlSessionFactoryBuilder依据传入的数据流生成Configuration目标,然后依据Configuration目标创立默认的SqlSessionFactory实例。

初始化的根本进程如下序列图所示:

MyBatis是如何初始化的

由上图所示,mybatis初始化要经过简略的以下几步:

  • 调用SqlSessionFactoryBuilder目标的build(inputStream)办法;

  • SqlSessionFactoryBuilder会依据输入流inputStream等信息创立XMLConfigBuilder目标;

  • SqlSessionFactoryBuilder调用XMLConfigBuilder目标的parse()办法;

  • XMLConfigBuilder目标回来Configuration目标;

  • SqlSessionFactoryBuilder依据Configuration目标创立一个DefaultSessionFactory目标;

  • SqlSessionFactoryBuilder回来 DefaultSessionFactory目标给Client,供Client运用。

SqlSessionFactoryBuilder相关的代码如下所示:

public SqlSessionFactory build(InputStream inputStream)  {
    return build(inputStream, null, null);  
}  
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)  {  
    try  {  
        //2. 创立XMLConfigBuilder目标用来解析XML装备文件,生成Configuration目标  
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);  
        //3. 将XML装备文件内的信息解析成Java目标Configuration目标  
        Configuration config = parser.parse();  
        //4. 依据Configuration目标创立出SqlSessionFactory目标  
        return build(config);  
    } 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内部经过Configuration目标来创立SqlSessionFactory,用户也能够自己经过API结构好Configuration目标,调用此办法创SqlSessionFactory  
public SqlSessionFactory build(Configuration config) {  
    return new DefaultSqlSessionFactory(config);  
}  

上述的初始化进程中,涉及到了以下几个目标:

  • SqlSessionFactoryBuilder : SqlSessionFactory的结构器,用于创立SqlSessionFactory,采用了Builder规划模式

  • Configuration :该目标是mybatis-config.xml文件中一切mybatis装备信息

  • SqlSessionFactory:SqlSession工厂类,以工厂方法创立SqlSession目标,采用了Factory工厂规划模式

  • XmlConfigParser :负责将mybatis-config.xml装备文件解析成Configuration目标,共SqlSessonFactoryBuilder运用,创立SqlSessionFactory

创立Configuration目标的进程

接着上述的 MyBatis初始化根本进程评论,当SqlSessionFactoryBuilder履行build()办法,调用了XMLConfigBuilder的parse()办法,然后回来了Configuration目标。那么parse()办法是怎么处理XML文件,生成Configuration目标的呢?

  • XMLConfigBuilder会将XML装备文件的信息转换为Document目标

而XML装备界说文件DTD转换成XMLMapperEntityResolver目标,然后将二者封装到XpathParser目标中,XpathParser的作用是供给依据Xpath表达式获取根本的DOM节点Node信息的操作。如下图所示:

MyBatis是如何初始化的

  • 之后XMLConfigBuilder调用parse()办法

会从XPathParser中取出<configuration>节点对应的Node目标,然后解析此Node节点的子Node:properties, settings, typeAliases,typeHandlers, objectFactory, objectWrapperFactory, plugins, environments,databaseIdProvider, mappers:

public Configuration parse() {
    if (parsed) {  
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");  
    }  
    parsed = true;  
    //源码中没有这一句,只有 parseConfiguration(parser.evalNode("/configuration"));  
    //为了让读者看得更明晰,源码拆分为以下两句  
    XNode configurationNode = parser.evalNode("/configuration");  
    parseConfiguration(configurationNode);  
    return configuration;  
}  
/** 
 * 解析 "/configuration"节点下的子节点信息,然后将解析的成果设置到Configuration目标中 
 */  
private void parseConfiguration(XNode root) {  
    try {  
        //1.首先处理properties 节点     
        propertiesElement(root.evalNode("properties")); //issue #117 read properties first  
        //2.处理typeAliases  
        typeAliasesElement(root.evalNode("typeAliases"));  
        //3.处理插件  
        pluginElement(root.evalNode("plugins"));  
        //4.处理objectFactory  
        objectFactoryElement(root.evalNode("objectFactory"));  
        //5.objectWrapperFactory  
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));  
        //6.settings  
        settingsElement(root.evalNode("settings"));  
        //7.处理environments  
        environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631  
        //8.database  
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));  
        //9.typeHandlers  
        typeHandlerElement(root.evalNode("typeHandlers"));  
        //10.mappers  
        mapperElement(root.evalNode("mappers"));  
    } catch (Exception e) {  
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);  
    }  
} 

注意:在上述代码中,还有一个非常重要的当地,便是解析XML装备文件子节点<mappers>的办法mapperElements(root.evalNode(“mappers”)), 它将解析咱们装备的Mapper.xml装备文件,Mapper装备文件能够说是MyBatis的核心,MyBatis的特性和理念都表现在此Mapper的装备和规划上。

  • 然后将这些值解析出来设置到Configuration目标中

解析子节点的进程这儿就不一一介绍了,用户能够参照MyBatis源码仔细揣摩,咱们就看上述的environmentsElement(root.evalNode(“environments”)); 办法是怎么将environments的信息解析出来,设置到Configuration目标中的:

/**
 * 解析environments节点,并将成果设置到Configuration目标中 
 * 注意:创立envronment时,假如SqlSessionFactoryBuilder指定了特定的环境(即数据源); 
 *      则回来指定环境(数据源)的Environment目标,否则回来默认的Environment目标; 
 *      这种办法实现了MyBatis能够衔接多数据源 
 */  
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))  
            {  
                //1.创立业务工厂 TransactionFactory  
                TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));  
                DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));  
                //2.创立数据源DataSource  
                DataSource dataSource = dsFactory.getDataSource();  
                //3. 结构Environment目标  
                Environment.Builder environmentBuilder = new Environment.Builder(id)  
                .transactionFactory(txFactory)  
                .dataSource(dataSource);  
                //4. 将创立的Envronment目标设置到configuration 目标中  
                configuration.setEnvironment(environmentBuilder.build());  
            }  
        }  
    }  
}
private boolean isSpecifiedEnvironment(String id)  
{  
    if (environment == null)  
    {  
        throw new BuilderException("No environment specified.");  
    }  
    else if (id == null)  
    {  
        throw new BuilderException("Environment requires an id attribute.");  
    }  
    else if (environment.equals(id))  
    {  
        return true;  
    }  
    return false;  
} 
  • 回来Configuration目标

将上述的MyBatis初始化根本进程的序列图细化:

MyBatis是如何初始化的

初始化办法 – 依据Java API

当然咱们能够运用XMLConfigBuilder手动解析XML装备文件来创立Configuration目标,代码如下:

String resource = "mybatis-config.xml";  
InputStream inputStream = Resources.getResourceAsStream(resource);  
// 手动创立XMLConfigBuilder,并解析创立Configuration目标  
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null,null); // 看这儿 
Configuration configuration = parser.parse();  
// 运用Configuration目标创立SqlSessionFactory  
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);  
// 运用MyBatis  
SqlSession sqlSession = sqlSessionFactory.openSession();  
List list = sqlSession.selectList("com.foo.bean.BlogMapper.queryAllBlogInfo");  

点击关注,第一时间了解华为云新鲜技能~