Mybatis源码分析(一)- Configuration配置文件详解

本系列以Mybatis 3.3.X分支源码作为分析源,mybatis源码git地址:https://github.com/mybatis/mybatis-3.git

mybatis的所有配置信息都存储到org.apache.ibatis.session.Configuration类中,可通过xml配置或者手动实例化获取到Configuration信息。

一、Configuration属性详解

Configuration类中属性定义主要有:Environment,Settings,Registry等

Environment描述:

   Environment主要用于配置数据源和事务信息。Mybatis支持多环境设置,可以为开发,测试,生产使用不同的配置。

   Environment的属性源码设置如下:

public final class Environment {
  //当前环境的唯一标识
  private final String id;
  //指定事务工厂
  private final TransactionFactory transactionFactory;
  //指定数据库数据源
  private final DataSource dataSource;
 Settings描述:

  Settings主要是一些运行时的全局设置,以下为Configuration中Settings可以配置的属性

//是否允许在嵌套语句中使用分页(RowBounds类主要包含offset和limit属性,用于分页使用)
  protected boolean safeRowBoundsEnabled = false;
  
  //是否允许在嵌套语句中使用分页(ResultHandler)
  protected boolean safeResultHandlerEnabled = true;
  
  //是否开启自动将数据库表字段映射为驼峰命名规则
  protected boolean mapUnderscoreToCamelCase = false;
  
  //该属性配置为true时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载。
  protected boolean aggressiveLazyLoading = true;
  
  //是否允许单一语句返回多结果集(需要兼容驱动)
  protected boolean multipleResultSetsEnabled = true;
  
  //允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)
  protected boolean useGeneratedKeys = false;
  
  //使用列标签代替列名
  protected boolean useColumnLabel = true;
  
  //缓存配置的全局开关
  protected boolean cacheEnabled = true;
  
  //指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。
  protected boolean callSettersOnNulls = false;


  //指定 MyBatis 增加到日志名称的前缀。
  protected String logPrefix;
  
  //指定 MyBatis 所用日志的具体实现,未指定时将自动查找
  protected Class <? extends Log> logImpl;
  
  //指定 VFS 的具体实现
  protected Class <? extends VFS> vfsImpl;
  
  //缓存作用域,分为LocalCacheScope.SESSION和LocalCacheScope.STATEMENT两种。
  //SESSION表示会缓存一个会话中执行的所有查询。 STATEMENT表示本地会话仅用在语句执行上,对相同 SqlSession的不同调用将不会共享数据。
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  
  //当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  
  //指定哪些方法触发一次延迟加载,当使用了集合中的方法时,会触发数据加载
  protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  
  //设置JDBC的queryTimeout超时时间
  protected Integer defaultStatementTimeout;
  
  //设置JDBC的fetchSize属性,用于限制最大获取数据量,防止某些查询返回数据量太大导致OutOfMemory
  protected Integer defaultFetchSize;
  
  //配置默认的执行器。SIMPLE 就是普通的执行器;REUSE会重用预处理语句(prepared statements); BATCH将重用语句并执行批量更新
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  
  //指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  
  //延迟加载的全局开关
  protected boolean lazyLoadingEnabled = false;
Registry描述:

Configuration中共定义了四个Registry,其中LanguageDriverRegistry驱动主要在Configuration构造函数中设置为XMLLanguageDriver, MapperRegistry用于自定义映射,下面详细描述TypeAliasRegistry和TypeHandlerRegistry:

1 TypeAliasRegistry  typeAliasRegistry(别名注册器)

    TypeAliasRegistry主要用来存储别名与JavaType类型的映射关系,内部使用了HashMap作为存储结构,并且在默认构造函数中注册了所有的基本类

    TypeAliasRegistry也支持用户自定义别名,其加载自定义别名的具体实现方法详见org.apache.ibatis.builder.xml.XMLConfigBuilder中的typeAliasesElement方法:型(详见TypeAliasRegistry的构造函数实现),  Configuration在其构造函数中同样注入了大量的全局别名信息,详见Configuration的默认构造器

//通过配置文件中加载出别名注册信息
  private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //如果配置为package,则将其注册为Object,此种情况下后续的解析会搜索此包下的所有JavaBean
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            if (alias == null) {
              //如果没有配置别名,则优先查看是否有Alias注解,否则采用对应clazz的名称表示
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

2 TypeHandlerRegistry  typeHandlerRegistry(类型处理注册器)

TypeHandlerRegistry  主要用于保存JDBC Type,JAVA Type与处理器的映射关系,各个Handler主要继承BaseTypeHandler,用户可以继承自定义实现,框架默认提供的Handler详见org.apache.ibatis.type包下的实现。其中TypeHandlerRegistry默认注册了常用类型的处理器映射,详见TypehandlerRegistry的默认构造函数。

自定义TypeHanlder详见org.apache.ibatis.builder.xml.XMLConfigBuilder中的typeHandlerElement方法

其他配置描述:

Configuration中还有其他配置信息,大多为容器信息,等后续分析使用时再详细描述。如果需要了解更多Mybatis配置信息,请参见Mybatis XML配置

二、XML中加载配置文件

配置信息可以手动构造Configuration对象也可通过XML配置,因为默认的SqlSessionFactoryBuilder采用配置文件方式读取配置,所以在此分析XML加载相关的源码。SqlSessionFactory(后续文章会分析此类)为整个框架的入口点,其主要生成方式采用SqlSessionFactoryBuilder的build构建,build方法作为我们分析的起点。

SqlSessionFactoryBuilder类:

SqlSessionFactoryBuilder中共有许多build重载方法,参数共有如下几种:配置文件输入流(Reader、InputStream), Properties和Environment

完整的build方法如下所示,主要调用XMLConfigBuilder的parse方法生成Configuration对象,并返回DefaultSqlSessionoFactory实例:

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.
      }
    }
其中参数描述如下:

enviroment参数用于指定当前使用的环境ID,其对应配置文件中ID节点,如下所示的配置环境信息:

<environments default="development">
  <environment id="development">
    <transactionManager type="JDBC">
        ...
    <dataSource type="POOLED">
        ...
  </environment>
  <environment id="production">
    <transactionManager type="MANAGED">
        ...
    <dataSource type="JNDI">
        ...
  </environment>
</environments>

如果配置文件中不存在此ID的节点,则会使用default=“development”指定的默认环境

Properties用于指定属性配置信息,共有三种地方可以设置,build的构造函数传入理解为Java代码传入配置信息,下面会在分析源码的地方进行Properties加载顺序说明。

XMLConfigBuilder类:

XMLConfigBuilder的构造函数主要使用了XPathParser对象,XPathParser中的createDocument方法实现了数据流转为XML节点的功能,主要使用了jdk自带的xml解析库,不了解的可以去百度下。

进入XMLConfigBuilder的parse方法,我们发现其主要调用了parseConfiguration进行解析,parseConfiguration完成了所有XML节点与Configuraton实例的一一解析映射:

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"));
      reflectionFactoryElement(root.evalNode("reflectionFactory"));
      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);
    }
  }
 以上代码中,参数root.evalNode中的参数名称即为XML节点的名称,均为根节点下的直属子节点,从名称可以看出节点的含义,可以第一节 Configuration配置详解中各个配置的描述,其中typeAliasesElement逻辑在第一节也分析过,其他类似。这里描述下propertiesElement方法中的逻辑,主要说明下Properties的加载顺序,详见mybatis官方文档描述:

  • 在 properties 元素体中指定的属性首先被读取,
  • 从 properties 元素的类路径 resource 或 url 指定的属性第二个被读取, 可以覆盖已经 指定的重复属性,
  • 作为方法参 数传递 的属性最 后被读 取,可以 覆盖已 经从 properties 元 素体和 resource/url 属性中加载的任意重复属性。
至于各个配置在何时使用,有什么作用,会在后续相关源码分析过程中一一说明










阅读更多
换一批

没有更多推荐了,返回首页