mybatis之Configuration解析

Posted 我爱看明朝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis之Configuration解析相关的知识,希望对你有一定的参考价值。

mybatis之Configuration解析

<?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>
    <property name= "" value "">
  </properties>

<!-- 这是mybatis中极为重要的调整,它会改变mybatis的运行时行为, name不能自己定义mybatis有内置的name  -->
  <settings>
    <setting name = "xxxx" value="" />
  </settings>

  <!-- 类型别名,可以为java类型设置一个缩写名字,仅用于xml中,意在降低冗余的书写   -->
  <typeAliases>
      <typeAlias alias = "xxx" type= "xx.xxx.xx" >
  </typeAliases>

  <!-- 自定义的类型处理器 -->
  <typeHandlers> 
    <typeHandler  handler = "xxxx" />
  </typeHandler>

  <!-- 自定义对象工厂, mybatis通过使用对象工厂,在获取结果集用来实例化对象 -->
  <objectFactory type = "xxxx">
  <!-- 可选  -->
    <property name = "xxx" value = "xxx" />
  </objectFactory>

  <!-- 自定义插件 -->
  <plugins>
    <plugin interceptor = "xxxx">
      <property name = "" value = "">
    </plugin>
  </plugins>


  <!-- 环境类型 -->
  <environments default = "dev">
    <environment id = "dev">
      <transactionManager type = "JDBC" >
        <properyt name = "..." vlaue= "...">
      </transactonManager>

      <dataSource type= "POOLED">  
        <property name= "..." value = "...">
      </dataSource>
    </envitonment>
  </environments>


  <!-- 数据库厂商标识 -->
  <databaseIdProvider type = "DB_VENDOR" >
      <property name = "..." value = "...">
  </databaseIdProvider>

  <!-- mappers映射器 -->
  <mappers>
    <mapper resource = "xxxx" />
    <mapper url = "xxx" />
    <package name = "xxxx"> 
  </mappers>

</configuration>

解析configuration的方法在XmlConfigurationBuilder中


public class XMLConfigBuiler extends BaseBuilder {

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

}

configuration总览

properties

在获取sqlSessionFactory传入properties属性的源码分析

// 1. 获取到了SqlSessionFactory传入properties, properties设置配置key和value
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, properties);

// 2.  调用方法: build(InputStream inputStream, String environment, Properties properties), 行为类是重载方法方式
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
}

class public SqlSesstionFactoryBuilder  {

//3. 实际调用的方法
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.
      }
    }
  }
    
}


public class  XMLConfigBuilder extends BaseBuilder {
    
    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        super(new Configuration());
        ErrorContext.instance().resource("SQL Mapper Configuration");
        // 方法传入的参数存放入Configuration的variables
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
  }
}


从xml文件读取properties属性

<configuration>
    <properties resource="org/mybatis/example/config.properties">
        <property name="username" value="dev_user"/>
        <property name="password" value="F2Fa3!33TYyg"/>
    </properties>
</configuration>

//XMLConfigBuilder
propertiesElement(root.evalNode("properties"));
//按照顺序加载属性


public class XMLConfiguration extends BaseBuilder {
    
  private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      // 1.解析出从xml中配置的属性
      Properties defaults = context.getChildrenAsProperties();
      //2.properties 可以配置resources或url任意属性(二者只能选其一)
      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.");
      }
      // Properties.putAll方法的底层是往HashTable中放key和value,同名key会覆盖值。
      //3. 如果有设置resource或url属性并且有值,则覆盖xml中的属性配置
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //4. 如果在获取sqlSessionFactory传入properties属性,则覆盖上面两步的属性值
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
      //更新configuration的variables
      configuration.setVariables(defaults);
    }
  }    
}

从上面源码我们得知Mybatis加载属性的顺序是:
1.首先读取xml在properties元素内指定的属性
2.根据properties中的属性resource或url读取,并覆盖上面的同名属性
3.最后读取方法参数传递的属性,并覆盖之前读取过的同名属性

从源码中学到的编码小技巧

根据XmlConfiguration的构造器的实现,我们可以学到复用重载方法
class public XMLConfigBuilder {
 public XMLConfigBuilder(Reader reader) {
    this(reader, null, null);
  }

  public XMLConfigBuilder(Reader reader, String environment) {
    this(reader, environment, null);
  }

  public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  public XMLConfigBuilder(InputStream inputStream) {
    this(inputStream, null, null);
  }

  public XMLConfigBuilder(InputStream inputStream, String environment) {
    this(inputStream, environment, null);
  }

  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

// 根据缺省参数来重载出不同的构造器
  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;
  }

}

settings

当在settings中设置了属性的值,它会改变Configuration中指定属性的值,这会改变Mybatis的运行行为。

 Properties settings = settingsAsProperties(root.evalNode("settings"));

public class XMLConfiBuilder extends BaseBuilder {

 private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    Properties props = context.getChildrenAsProperties();
    //检查settings配置的属性是否存在于Configuration的属性
    //使用mybatsi封装的反射方法
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    for (Object key : props.keySet()) {
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

 //根据读取到的Settings配置设置属性值,没有的则设置默认值
  private void settingsElement(Properties props) throws Exception {
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.以上是关于mybatis之Configuration解析的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis之Configuration初始化(配置文件.xml的解析)

mybatis源码解析之Configuration加载

mybatis源码-解析配置文件之配置文件Mapper解析

五Mybatis 核心对象之 Configuration 对象初始化详解

精通Mybatis之Configuration配置体系

精通Mybatis之Configuration配置体系