04 Mybatis源码篇---XML配置之environments环境

前面研究了一下MyBatis中TypeAliases类型命名的注册过程,下面研究一下MyBatis中XML配置environments环境来创建事务和数据源的流程。

还是先从解析MyBatis配置文件configuration节点及子节点时,调用environmentsElement来解析environments。

      /**
       * MyBatis配置环境,MyBatis支持多种环境,这中机制适用于多种数据库之中
       */
      environmentsElement(root.evalNode("environments"));


直接看源码,我以注释的形式进行解释说明。

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      /**
       * 如果我们在方法中没有传递environment参数,就解析MyBatis-config配置文件的
       */
      if (environment == null) {
        /**
         *  获取配置文件default的值<environments default="development">
         *  environment = development
         */
        environment = context.getStringAttribute("default");
      }
      /**
       * 解析environment节点以及一下节点,这里可以说明,Mybatis是支持双数据源的
       */
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        /**
         *
         * isSpecifiedEnvironment 判断 environment 和 id是否为空,如果为空,抛出一场。如果相等返回true,否则返回false
         *
         */
        if (isSpecifiedEnvironment(id)) {
          /**
           * <transactionManager type="JDBC"/>
           * 事务
           */
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          /**
           * <dataSource type="POOLED">
           *数据源
           */
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          /**
           * 将dataSource设置进configuration对象
           */
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }


事物的声明:

 private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      /**
       *  <transactionManager type="JDBC"/>
       *  MyBatis支持两种的事物管理,也就是 type="[JDBC|MANAGED]"
       *    JDBC --- 这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的链接来管理事物的作用域
       *    MANAGED ---- 这个配置从来不提交或者回滚一个链接,而是让容器来管理事物的整个生命周期(eg JEE应用服务器的上下文) 默认情况下它会关闭,然而一些容器并不希望这样,因此需要将closeConnection 属性设置为false,来阻止它的默认关闭。
       *      eg:<transactionManager type="MANAGED">
                    <property name="closeConnection" value="false"/>
                 </transactionManager>
       */
      String type = context.getStringAttribute("type");
      /**
       * 获取transactionMagger 中properties属性
       */
      Properties props = context.getChildrenAsProperties();
      /**
       * resolveClass(type)这个我们主要说一下,回想一下我们创建Configuration对象时他的默认构造方法给类型命名,有如下代码:
       *    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
       *    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
       *resolveClass(type)返回JdbcTransactionFactory Calss,使用newInstance通过反射创建TransactionFactory对象
       */
      TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }


数据源:

 private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      /**
       * <dataSource type="POOLED">
       * MyBatis有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):
       *  UNPOOLED--不使用任何数据库连接池来管理数据库连接
       *  POOLED--使用Mybatis自带的数据库连接池来管理数据库连接
       *  JNDI:这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
       */
      String type = context.getStringAttribute("type");
      /**
       *   <property name="driver" value="${driver}"/>
       *   <property name="url" value="${url}"/>
       *   <property name="username" value="${username}"/>
       *   <property name="password" value="${password}"/>
       */
      Properties props = context.getChildrenAsProperties();
      /**
       * 这里直接参考事物的讲解
       *  typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
       *  typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
       *  typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
       */
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

以上我们简单的看了一下通过解析environments节点以及一下子节点,来初始化数据源和事物,但是MyBatis是如何通过${driver}这个获取对应的参数呢?我们看如下代码


/**
 * @author Clinton Begin
 * @author Kazuki Shimizu
 * 解析${}这种形式的表达式
 */
public class PropertyParser {

  private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
  /**
   * The special property key that indicate whether enable a default value on placeholder.
   * <p>
   *   The default value is {@code false} (indicate disable a default value on placeholder)
   *   If you specify the {@code true}, you can specify key and default value on placeholder (e.g. {@code ${db.username:postgres}}).
   * </p>
   * @since 3.4.2
   */
  public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";

  /**
   * The special property key that specify a separator for key and default value on placeholder.
   * <p>
   *   The default separator is {@code ":"}.
   * </p>
   * @since 3.4.2
   */
  public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";

  private static final String ENABLE_DEFAULT_VALUE = "false";
  private static final String DEFAULT_VALUE_SEPARATOR = ":";

  private PropertyParser() {
    // Prevent Instantiation
  }

  public static String parse(String string, Properties variables) {
    VariableTokenHandler handler = new VariableTokenHandler(variables);
    GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
    return parser.parse(string);
  }

  private static class VariableTokenHandler implements TokenHandler {
    private final Properties variables;
    private final boolean enableDefaultValue;
    private final String defaultValueSeparator;

    private VariableTokenHandler(Properties variables) {
      this.variables = variables;
      this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
      this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
    }

    private String getPropertyValue(String key, String defaultValue) {
      return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
    }

    @Override
    public String handleToken(String content) {
      if (variables != null) {
        String key = content;
        if (enableDefaultValue) {
          final int separatorIndex = content.indexOf(defaultValueSeparator);
          String defaultValue = null;
          if (separatorIndex >= 0) {
            key = content.substring(0, separatorIndex);
            defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
          }
          if (defaultValue != null) {
            return variables.getProperty(key, defaultValue);
          }
        }
        if (variables.containsKey(key)) {
          return variables.getProperty(key);
        }
      }
      return "${" + content + "}";
    }
  }

}

MyBaits通过PropertyParser这个类解析${driver}表达式。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值