Mybatis源码学习(二)配置文件解析到SqlSessionFactory构建

前言

上一篇文章中简单的介绍了Mybatis的执行流程.
执行的第一步就是构建一个SqlSessionFactory, 那到底我们是如何获取到SqlSessionFactory的呢?
接下来让我们一起学习一下吧!

1.SqlSessionFactory源码详解

SqlSessionFactory是一个接口,引用代码的文档解释: 它的作用就是从Connection或者DataSource中创建一个SqlSession
它有两个实现DefaultSqlSessionFactorySqlSessionManager

public interface SqlSessionFactory {

  /**获取一个默认配置的SqlSession
   * @return              默认配置的SqlSession
   */
  SqlSession openSession();

  /**获取一个指定事务提交方式的SqlSession
   * @param autoCommit    事务提交方式, true,false 自动或手动
   * @return              指定事务提交方式的SqlSession
   */
  SqlSession openSession(boolean autoCommit);

  /**获取一个给定数据库连接的SqlSession
   * @param connection    数据库连接
   * @return              指定数据库连接的SqlSession
   */
  SqlSession openSession(Connection connection);

  /**获取一个给定事务隔离级别的SqlSession
   * @param level         事务隔离级别
   * @return              给定事务隔离级别的SqlSession
   */
  SqlSession openSession(TransactionIsolationLevel level);

  /**获取一个给定执行器类型的SqlSession
   * @param execType      DefaultSqlSession中维护的一个执行器的类型
   * @return              给定执行器类型的SqlSession
   */
  SqlSession openSession(ExecutorType execType);

  /**获取一个给定执行器类型和事务提交方式的SqlSession
   * @param execType      DefaultSqlSession中维护的一个执行器的类型
   * @param autoCommit    事务提交方式, true,false 自动或手动
   * @return              给定执行器类型和事务提交方式的SqlSession
   */
  SqlSession openSession(ExecutorType execType, boolean autoCommit);

  /**获取一个给定执行器类型和事务隔离级别的SqlSession
   * @param execType      DefaultSqlSession中维护的一个执行器的类型
   * @param level         事务隔离级别
   * @return              给定执行器类型和事务隔离级别的SqlSession
   */
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

  /**获取一个给定执行器类型和数据库连接的SqlSession
   * @param execType      DefaultSqlSession中维护的一个执行器的类型
   * @param connection    数据库连接
   * @return              给定执行器类型和数据库连接的SqlSession
   */
  SqlSession openSession(ExecutorType execType, Connection connection);

  /**获取核心配置文件对象
   * @return              核心配置文件对象
   */
  Configuration getConfiguration();

}

1.1 DefaultSqlSessionFactory源码详解

DefaultSqlSessionFactorySqlSessionFactory的默认实现,接口的openSession(xxx …)的实现都是通过内部的两个函数实现
openSessionFromDataSource()openSessionFromConnection()
这也和接口的文档上描述的一致
在这里插入图片描述
openSessionFromDataSource()和openSessionFromConnection() 的实现对比
他们的主体实现基本一致,区别是在于事务的构造上
openSessionFromDataSource 是通过数据源和参数指定的事务隔离级别,参数指定的是否自动提交来构建事务
openSessionFromConnection 是通过连接中的属性来构建事务
它们最终返回的都是
DefaultSqlSession

/**从数据源中获取SqlSession
   * @param execType      执行器的类型
   * @param level         事务隔离级别
   * @param autoCommit    事务提交方式, true,false 自动或手动
   * @return              DefaultSqlSession
   */
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      //读取环境参数
      final Environment environment = configuration.getEnvironment();
      //获取事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //使用事务工厂创建事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //创建一个sql执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      //构建一个DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      //如果失败,关闭事务
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  /**从连接中获取SqlSession
   * @param execType      执行器的类型
   * @param connection    数据库连接
   * @return              DefaultSqlSession
   */
  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
    try {
      boolean autoCommit;
      try {
        //从连接中获取事务提交类型
        autoCommit = connection.getAutoCommit();
      } catch (SQLException e) {
        // Failover to true, as most poor drivers
        // or databases won't support transactions
        //默认为true, 有的数据库或驱动不支持事务
        autoCommit = true;
      }
      //读取环境参数
      final Environment environment = configuration.getEnvironment();
      //获取事务工厂
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //使用事务工厂创建事务
      final Transaction tx = transactionFactory.newTransaction(connection);
      //创建一个sql执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      //构建一个DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

1.2 SqlSessionManager源码详解

SqlSessionManager实现了SqlSessionFactory和SqlSession两个接口,本章我们主要学习下SqlSessionFactory相关的接口.
后续再进行SqlSession的学习

在这里插入图片描述
SqlSessionManager内部维护了一个SqlSessionFactory和一个SqlSession对象还有一个ThreadLocal变量,他提供的newInstance()方法都是创建一个DefaultSqlSessionFactory,然后调用私有的构造方法进行构造.所以他的openSession()相关方法的实现都是通过DefaultSqlSessionFactory去实现的
在这里插入图片描述
从上述的学习我们可以发现DefaultSqlSessionFactory目前都是通过SqlSessionFactoryBuilder的build()方法构建的,接下来我们就来学习SqlSessionFactoryBuilder

2. SqlSessionFactoryBuilder源码详解

打开SqlSessionFactory源码, 我们可以看见提供了build()方法的9种重载方式
在这里插入图片描述

具体可以分为以下三类

1. 以Reader为基础

 前四个方法都是以Reader为基础的, 真正逻辑的实现在第四个方法,前三个方法通过传递默认参数null进行内部调用
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
   }
}

2. 以InputStream为基础

 第五到第八个方法都是以InputStream为基础的, 真正逻辑的实现在第八个方法,前三个方法通过传递默认参数null进行内部调用
 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.
      }
    }
 }

3. 以Configuration为基础

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

其实第一种和第二种的实现方式都是类似,通过构建XMLConfigBuilder,然后调用parse()方法获得一个Configuration对象.
最终调用第三种方式获取SqlSessionFactory

自己YY

第二种方式也可以改造成如下方式, 通过InputStreamReader来包装InputStream的方式获得一个Reader,然后调用以Reader为基础的方法进行操作

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    return build(new InputStreamReader(inputStream), environment, properties);
  }

3. XMLConfigBuilder源码详解

我们对XMLConfigBuilder进行简单的分解

  1. 六个公共的构造方法提供调用
  2. 一个私有的构造方法,是仅供内部调用, 六个公共的构造方法最终都是通过私有方法去实现初始化
  3. 一个公共的parse()方法,调用了内部的N个私有方法
  4. 一堆私有的方法, 在parse()中被调用
    在这里插入图片描述

1.私有的构造方法解析

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

在这个构造方法中主要做了两件事

  1. 调用父类的构造方法,并创建一个Configuration对象传入
  2. 将参数设置到属性中

2.parse()方法解析

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

在parse()方法中完成的逻辑

  1. 判断是否解析过,没解析则进行解析,并返回Configuration对象
  2. 如果已经被解析过,则抛出异常,一个XMLConfigBuilder只能解析一次

3.parseConfiguration(XNode root)方法解析

private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      //解析properties然后放入Configuration中
      propertiesElement(root.evalNode("properties"));
      //解析settings
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      //VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源
      loadCustomVfs(settings);
      //自定义log实现
      loadCustomLogImpl(settings);
      //处理别名
      typeAliasesElement(root.evalNode("typeAliases"));
      //处理插件
      pluginElement(root.evalNode("plugins"));
      //处理自定义对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      //处理自定义对象包装工厂
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //处理反射工厂
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      //解析自定义setting,并设置到Configuration
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      //解析环境配置
      environmentsElement(root.evalNode("environments"));
      //解析数据库提供厂商
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //解析类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //解析自定义mapper
      mapperElement(root.evalNode("mappers"));
      
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

4. 解析Properties

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      //读取所有子节点构建一个Properties, 遍历读取name和value属性,然后进行赋值
      Properties defaults = context.getChildrenAsProperties();
      //读取resource
      String resource = context.getStringAttribute("resource");
      //读取url
      String url = context.getStringAttribute("url");
      //url和resource不能共存
      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) {
      	//从resource中解析Properties,并放入默认的Properties
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
      	//从url中解析Properties,并放入默认的Properties
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      //合并configuration中已存在的配置和默认配置,并设置到configuration中
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

5. 解析Setting

private Properties settingsAsProperties(XNode context) {
	//为空,返回一个默认全空的Properties
    if (context == null) {
      return new Properties();
    }
    //读取所有子节点作为Properties
    Properties props = context.getChildrenAsProperties();
    //检查所有的配置能被Configuration识别
    // Check that all settings are known to the configuration class
    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;
}

6. 根据Setting设置虚拟文件系统VFS

private void loadCustomVfs(Properties props) throws ClassNotFoundException {
	//获取虚拟文件的自定义实现类
    String value = props.getProperty("vfsImpl");
    if (value != null) {
      String[] clazzes = value.split(",");
      for (String clazz : clazzes) {
        if (!clazz.isEmpty()) {
          @SuppressWarnings("unchecked")
          //反射创建虚拟文件的自定义实现类,并设置到configuration
          Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
          configuration.setVfsImpl(vfsImpl);
        }
      }
    }
}

7.根据Setting设置LogImpl

private void loadCustomLogImpl(Properties props) {
	//解析LogImpl
    Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
    //设置LogImpl
    configuration.setLogImpl(logImpl);
}

其中resolveClass(props.getProperty(“logImpl”))的底层是调用了TypeAliasRegistry的resolveAlias(String string) 实现

public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      //typeAliases是一个维护了常用的java类的一个Map, 是在TypeAliasRegistry的构造函数中进行的初始化赋值
      //使用常用类是不用每次通过反射创建Class对象
      if (typeAliases.containsKey(key)) {
        value = (Class<T>) typeAliases.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
}

8. 解析别名

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          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) {
              typeAliasRegistry.registerAlias(clazz);
            } else {
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
}
8.1 通过包名注册别名
public void registerAliases(String packageName, Class<?> superType) {
  ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
  resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
  Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
  for (Class<?> type : typeSet) {
    // Ignore inner classes and interfaces (including package-info.java)
    // Skip also inner classes. See issue #6
    if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
      registerAlias(type);
    }
  }
}

ResolverUtil的find()方法

public ResolverUtil<T> find(Test test, String packageName) {
  //包名解析为路径名
  String path = getPackagePath(packageName);
  try {
  	//通过文件系统获取所有的路径
    List<String> children = VFS.getInstance().list(path);
    for (String child : children) {
      if (child.endsWith(".class")) {
      	//找到所有.class文件,然后进行匹配,匹配成功加入到内部维护的Set<Class<? extends T>> matches
        addIfMatching(test, child);
      }
    }
  } catch (IOException ioe) {
    log.error("Could not read package: " + packageName, ioe);
  }
  return this;
}
8.2 单个别名的注册
if (alias == null) {
	typeAliasRegistry.registerAlias(clazz);
} else {
  	typeAliasRegistry.registerAlias(alias, clazz);
}

如果别名未给定
获取有没有Alias.class注解,如果有注解,则用注解的值作为别名,否则取类型名字作为别名

public void registerAlias(Class<?> type) {
   String alias = type.getSimpleName();
   Alias aliasAnnotation = type.getAnnotation(Alias.class);
   if (aliasAnnotation != null) {
     alias = aliasAnnotation.value();
   }
   registerAlias(alias, type);
}

给定别名的情况
如果别名已经被注册并且重新注册的值和之前不同,抛出异常

public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
    }
    typeAliases.put(key, value);
}

9. 解析自定义拦截器

private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
      	//解析所有的interceptor
        String interceptor = child.getStringAttribute("interceptor");
        //即系interceptor下的属性值
        Properties properties = child.getChildrenAsProperties();
        //反射获取Interceptor的实例
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
        //设置属性
        interceptorInstance.setProperties(properties);
        //添加interceptor
        configuration.addInterceptor(interceptorInstance);
      }
    }
}

10. 解析对象工厂

private void objectFactoryElement(XNode context) throws Exception {
    if (context != null) {
      //解析type
      String type = context.getStringAttribute("type");
	  //解析对象工厂的属性
      Properties properties = context.getChildrenAsProperties();
      //根据type解析对象工厂类
      ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      //设置属性
      factory.setProperties(properties);
      //设置对象工厂
      configuration.setObjectFactory(factory);
    }
}

11.解析对象包装工厂

private void objectWrapperFactoryElement(XNode context) throws Exception {
    if (context != null) {
       //解析type
      String type = context.getStringAttribute("type");
      //根据type解析对象包装工厂类
      ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      //设置对象包装工厂
      configuration.setObjectWrapperFactory(factory);
    }
}

12. 解析反射工厂

private void reflectorFactoryElement(XNode context) throws Exception {
    if (context != null) {
   	  //解析type
      String type = context.getStringAttribute("type");
      //根据type解析反射工厂类
      ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      //设置对象反射工厂类
      configuration.setReflectorFactory(factory);
    }
  }

13. 设置Configuration的各个属性

private void settingsElement(Properties props) {
    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.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
    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.getProperty("safeResultHandlerEnabled"), true));
    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
    configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
  }

14.设置Environment

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)) {
          //事务工厂
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //数据源工厂
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          //构建数据源
          DataSource dataSource = dsFactory.getDataSource();
          //构建Environment
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          //设置Environment
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

15. 解析数据库厂商

private void databaseIdProviderElement(XNode context) throws Exception {
    DatabaseIdProvider databaseIdProvider = null;
    if (context != null) {
      String type = context.getStringAttribute("type");
      // awful patch to keep backward compatibility
      if ("VENDOR".equals(type)) {
        type = "DB_VENDOR";
      }
      Properties properties = context.getChildrenAsProperties();
      databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance();
      databaseIdProvider.setProperties(properties);
    }
    Environment environment = configuration.getEnvironment();
    if (environment != null && databaseIdProvider != null) {
      String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
      configuration.setDatabaseId(databaseId);
    }
}

16. 解析TypeHandler

private void typeHandlerElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //指定包名方式配置
          String typeHandlerPackage = child.getStringAttribute("name");
          //指定包下进行扫描,找到所有为TypeHandler.class的class,然后进行注册
          //会排除接口,匿名内部类,还有抽象类
          typeHandlerRegistry.register(typeHandlerPackage);
        } else {
          //指定单个的处理器
          String javaTypeName = child.getStringAttribute("javaType");
          String jdbcTypeName = child.getStringAttribute("jdbcType");
          String handlerTypeName = child.getStringAttribute("handler");
          Class<?> javaTypeClass = resolveClass(javaTypeName);
          JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
          Class<?> typeHandlerClass = resolveClass(handlerTypeName);
          if (javaTypeClass != null) {
            if (jdbcType == null) {
              typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
            } else {
              typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
            }
          } else {
            typeHandlerRegistry.register(typeHandlerClass);
          }
        }
      }
    }
}

17.解析所有的Mapper配置

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //通过包名配置
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          //优先从resource进行解析
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
          	//其次从url解析
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
          	//最后从class解析
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
}
17.1 package 配置 和 class配置

package配置通过扫描包名,找到所有的.class文件,然后调用MapperRegistryaddMapper方法
class配置通过反射获取Mapper类,然后调用MapperRegistryaddMapper方法

public <T> void addMapper(Class<T> type) {
	//只对指定包下的接口进行处理
    if (type.isInterface()) {
      //如果重复解析,会抛出异常
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
      	//为每个Interface生成一个代理工厂MapperProxyFactory,后续接口的方法执行时获通过代理工厂生成的代理进行执行
      	//后续章节我们再详细的对MapperProxyFactory进行解析
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        //解析类中的方法和注解等
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          //如果加载未完成,map中移除该对象
          knownMappers.remove(type);
        }
      }
    }
}
17.2 resource 配置和url 配置

都是通过构造XMLMapperBuilder,调用parse()方法进行解析

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //解析mapper文件的各个节点
      configurationElement(parser.evalNode("/mapper"));
      //加载资源
      configuration.addLoadedResource(resource);
      //绑定命名空间和Mapper,最终调用MapperRegistry的addMapper方法
      bindMapperForNamespace();
    }
	//解析resultMap
    parsePendingResultMaps();
    //解析缓存
    parsePendingCacheRefs();
    //解析语句
    parsePendingStatements();
}

总结

在这里插入图片描述

通过上述的代码学习,可以发现,大量的工作都是在一开始的xml解析.通过xml解析之后,获取Configuration对象,然后构造SqlSessionFactory.文中并没有对XMLMapperBuilder和MapperAnnotationBuilder中的sql语句解析等进行详细的学习感兴趣的读者可以留言,也可以专门写一遍解析的文章,大家一起学习!

喜欢的小伙伴请动动小手关注和点赞吧,也可留言一起探讨怎样更好的学习源码!!!

文章链接

Mybatis源码学习(一)初探执行流程
Mybatis源码学习(二)配置文件解析到SqlSessionFactory构建
Mybatis源码学习(三)SqlSession详解
Mybatis源码学习(四)自定义Mapper方法执行流程
Mybatis源码学习(五)Executor和StatementHandler详解
Mybatis源码学习(六)结果集自动封装机制
Mybatis源码学习(七)mybatis缓存详解
Mybatis源码学习(八)Mybatis设计模式总结

学习资料整理

本人作为Java开发菜鸡,平时也收集了很多学习视频,在此分享给大家一起学习

整套VIP学习视频

在这里插入图片描述

架构师相关视频

在这里插入图片描述

扫码领取

在这里插入图片描述

更多资料链接

Java免费学习视频下载
Python免费学习视频下载
Web前端免费学习视频下载
人工智能免费学习视频下载
大数据免费学习视频下载
UI设计免费学习视频下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值