MyBatis源码-配置篇(一)

MyBatis源码-配置原理篇(一)

  1. 先看个简单的配置:
    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>
    <properties resource="jdbc.properties"/>
    <!--<typeAliases>-->
        <!--<typeAlias alias="employee" type="Employee"/>-->
    <!--</typeAliases> -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!--配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${database.driver}"/>
                <property name="url" value="${database.url}"/>
                <property name="username" value="${database.username}"/>
                <property name="password" value="${database.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入我们自己写的每一个接口的实现文件名称-->
    <mappers>
        <!--resource标识从类路径下找资源-->
        <!--<package name="mapper"/>-->
        <mapper resource="mapper/UserCardMapper.xml"/>
        <mapper resource="mapper/UserMapper.xml"/>
        <mapper resource="mapper/UserAuthMapper.xml"/>
        <mapper resource="mapper/PayOrderMapper.xml"/>
    </mappers>
</configuration>

jdbc.properties

database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhost:3306/yolyn_authpay?serverTimezone=UTC&characterEncoding=UTF-8
database.username=root
database.password=root

我们来看看配置是怎么解析的

在读取配置的时候我们会写下下面几行代码:

public class SqlSessionUtil {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
             sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

build方法点进去

public SqlSessionFactory build(InputStream inputStream) {
  return build(inputStream, null, null);
}

再进build方法

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

这里可以看到创建了一个XMLConfigBuilder来解析这个文件流,然后传到build方法,先看下这个build方法再进parse方法看看:
build方法

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

注意看下这个build方法传入的是啥,一个Configuration实例,那说明XMLConfigBuilder.parse()方法返回的是一个Configuration,看下parse方法

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

看返回的是啥,啊哈,一个Configuration,并且这里用了一个parsed变量保证配置文件只解析一次,parsed初始值为false,看下这个parseConfiguration方法


private void parseConfiguration(XNode root) {
  try {
    //issue #117 read properties first
    propertiesElement(root.evalNode("properties"));//获取额外的properties配置
    Properties settings = settingsAsProperties(root.evalNode("settings"));
    loadCustomVfs(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);
  }
}

激不激动,刺不刺激,这里可干了好多事情哦,我们挑几个看下:

    1. propertiesElement(root.evalNode(“properties”));
      获取并解析额外的配置文件,我们在这个例子一开始写了一个jdbc连接配置,我们看下这个方法:
private void propertiesElement(XNode context) throws Exception {
  if (context != null) {
    Properties defaults = context.getChildrenAsProperties();
    String resource = context.getStringAttribute("resource");
    String url = context.getStringAttribute("url");
    if (resource != null && url != null) {
      throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");//不能同时存在两个配置源
    }
    if (resource != null) {
      defaults.putAll(Resources.getResourceAsProperties(resource));
    } else if (url != null) {
      defaults.putAll(Resources.getUrlAsProperties(url));
    }
    Properties vars = configuration.getVariables();
    if (vars != null) {
      defaults.putAll(vars);
    }
    parser.setVariables(defaults);
    configuration.setVariables(defaults);
  }
}

我们要知道这个Properties是一个HashTable,解析后的配置以键值对的形式保存在这个HashTable里面,不信的话可以看下getResourceAsProperties方法

getResourceAsProperties方法

public static Properties getResourceAsProperties(String resource) throws IOException {
  Properties props = new Properties();
  InputStream in = getResourceAsStream(resource);
  props.load(in);
  in.close();
  return props;
}

public synchronized void load(InputStream inStream) throws IOException {
    load0(new LineReader(inStream));
}

private void load0 (LineReader lr) throws IOException {
                //此处省略好多代码
        String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
        String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
        put(key, value);
    }
}

看到了吧,还有啥好说的,就是这样,到这里一定好奇一开始配置连接池的时候,额外配置文件jdbc.properties的值是怎么塞进去的吧,我们再看下

<environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!--配置连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${database.driver}"/>
                <property name="url" value="${database.url}"/>
                <property name="username" value="${database.username}"/>
                <property name="password" value="${database.password}"/>
            </dataSource>
        </environment>
    </environments>

哎呀,自己看去吧,懒得扒了。。。
反正这些配置相关的东西大多最终都是放在Configuration里面,包括Mapper,不信你自己看去,除此之外还有些东西是放在map里面,如TypeHandler啦,不信你可以去看XMLConfigBuilder的父类BaseBuilder中的TypeHandlerRegistry

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值