【源码分析】Mybatis 的配置解析过程

news2024/10/6 6:49:48

博主介绍: ✌博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家✌

Java知识图谱点击链接:体系化学习Java(Java面试专题)

💕💕 感兴趣的同学可以收藏关注下不然下次找不到哟💕💕

在这里插入图片描述

文章目录

  • 1、配置解析方法
  • 2、propertiesElement(context.evalNode("properties"))
  • 3、loadCustomVfs(settings)
  • 4、loadCustomLogImpl(settings)
  • 5、typeAliasesElement(context.evalNode("typeAliases"))
  • 6、pluginElement(context.evalNode("plugins"))
  • 7、 objectFactoryElement(context.evalNode("objectFactory"))
  • 8、objectWrapperFactoryElement(context.evalNode("objectWrapperFactory"))
  • 9、reflectorFactoryElement(context.evalNode("reflectorFactory"))
  • 10、settingsElement(settings)
  • 11、environmentsElement(context.evalNode("environments"))
  • 12、 databaseIdProviderElement(context.evalNode("databaseIdProvider"))
  • 13、 typeHandlerElement(context.evalNode("typeHandlers"))
  • 14、mapperElement(context.evalNode("mappers"))

1、配置解析方法

在我的另一篇文章:深入学习 Mybatis 的四大组件源码 中有介绍到 XMLConfigBuilder.parse方法,这个方法是 MyBatis 中用于解析配置文件的核心方法之一。下面是对该方法的源码分析和注释:

public Configuration parse() {
  // 如果configuration对象为空,就创建一个新的对象
  if (!parsed) {
    parsed = true;
    // 调用parseConfiguration方法解析配置文件
    configurationElement(parser.evalNode("/configuration"));
  }
  return configuration;
}

// 解析配置文件
private void configurationElement(XNode context) {
  try {
    // 创建一个Configuration对象
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    // 解析properties节点
    propertiesElement(context.evalNode("properties"));
    // 解析settings节点
    Properties settings = settingsAsProperties(context.evalNode("settings"));
    loadCustomVfs(settings);
    loadCustomLogImpl(settings);
    // 解析typeAliases节点
    typeAliasesElement(context.evalNode("typeAliases"));
    // 解析plugins节点
    pluginElement(context.evalNode("plugins"));
    // 解析objectFactory节点
    objectFactoryElement(context.evalNode("objectFactory"));
    // 解析objectWrapperFactory节点
    objectWrapperFactoryElement(context.evalNode("objectWrapperFactory"));
    // 解析reflectorFactory节点
    reflectorFactoryElement(context.evalNode("reflectorFactory"));
    // 将settings节点中的属性设置到Configuration对象中
    settingsElement(settings);
    // 解析environments节点
    environmentsElement(context.evalNode("environments"));
    // 解析databaseIdProvider节点
    databaseIdProviderElement(context.evalNode("databaseIdProvider"));
    // 解析typeHandlers节点
    typeHandlerElement(context.evalNode("typeHandlers"));
    // 解析mappers节点
    mapperElement(context.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

// 解析mappers节点
private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    // 遍历mappers节点下的所有子节点
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        // 如果子节点是package,就扫描指定包下的所有映射器
        String mapperPackage = child.getStringAttribute("name");
        builderAssistant.addMapper(mapperPackage);
      } else {
        // 如果子节点是mapper,就解析指定的映射器文件
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, builderAssistant.getConfiguration(), resource, builderAssistant.getConfiguration().getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          InputStream inputStream = Resources.getUrlAsStream(url);
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, builderAssistant.getConfiguration(), url, builderAssistant.getConfiguration().getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          // 如果指定了映射器接口,就将其添加到Configuration对象中
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          builderAssistant.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

XMLConfigBuilder.parse方法的作用是解析MyBatis配置文件,该方法首先会创建一个新的Configuration对象,然后解析配置文件中的各个节点,并将解析结果设置到Configuration对象中。其中,configurationElement方法是解析配置文件的核心方法,它会依次解析properties、settings、typeAliases、plugins、objectFactory、objectWrapperFactory、reflectorFactory、environments、databaseIdProvider、typeHandlers和mappers节点。对于mappers节点,如果子节点是package,就会扫描指定包下的所有映射器;如果子节点是mapper,就会解析指定的映射器文件或映射器接口,并将其添加到Configuration对象中。在解析过程中,如果出现异常,就会抛出BuilderException异常。

核心方法是以下这一段:

 // 解析properties节点
propertiesElement(context.evalNode("properties"));
// 解析settings节点
Properties settings = settingsAsProperties(context.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 解析typeAliases节点
typeAliasesElement(context.evalNode("typeAliases"));
// 解析plugins节点
pluginElement(context.evalNode("plugins"));
// 解析objectFactory节点
objectFactoryElement(context.evalNode("objectFactory"));
// 解析objectWrapperFactory节点
objectWrapperFactoryElement(context.evalNode("objectWrapperFactory"));
// 解析reflectorFactory节点
reflectorFactoryElement(context.evalNode("reflectorFactory"));
// 将settings节点中的属性设置到Configuration对象中
settingsElement(settings);
// 解析environments节点
environmentsElement(context.evalNode("environments"));
// 解析databaseIdProvider节点
databaseIdProviderElement(context.evalNode("databaseIdProvider"));
// 解析typeHandlers节点
typeHandlerElement(context.evalNode("typeHandlers"));
// 解析mappers节点
mapperElement(context.evalNode("mappers"));

接下来我们就一点点的看解析的步骤!

2、propertiesElement(context.evalNode(“properties”))

先看这段:

// 解析properties节点
propertiesElement(context.evalNode("properties"));

MyBatis中XMLConfigBuilder类中的一个私有方法 propertiesElement ,用于解析配置文件中的properties节点。下面是对该方法的详细解释:

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
        // 获取properties节点下的所有子节点
        Properties defaults = context.getChildrenAsProperties();
        // 获取properties节点的resource属性
        String resource = context.getStringAttribute("resource");
        // 获取properties节点的url属性
        String url = context.getStringAttribute("url");
        // 如果resource和url都为空,就直接使用defaults中的属性
        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中加载属性
            defaults.putAll(Resources.getResourceAsProperties(resource));
        } else if (url != null) {
            // 从指定的url中加载属性
            defaults.putAll(Resources.getUrlAsProperties(url));
        }
        // 将解析出来的属性设置到Configuration对象中
        Properties vars = configuration.getVariables();
        if (vars != null) {
            defaults.putAll(vars);
        }
        parser.setVariables(defaults);
        configuration.setVariables(defaults);
    }
}

该方法首先判断properties节点是否存在,如果存在则获取其下的所有子节点,并将其转换为Properties对象。接着,该方法会获取properties节点的resource和url属性,如果两个属性都为空,则直接使用子节点中解析出来的属性。如果resource和url属性中有一个不为空,则从指定的资源(文件或URL)中加载属性,并将其合并到子节点中解析出来的属性中。最后,该方法将合并后的属性设置到Configuration对象中,以供后续的数据库操作使用。

举个例子,假设我们在MyBatis的配置文件中定义了一个properties节点,如下所示:

<properties resource="db.properties">
  <property name="username" value="root" />
  <property name="password" value="123456" />
</properties>

其中,resource属性指定了db.properties文件的路径,同时在properties节点下还定义了两个property子节点,分别表示数据库用户名和密码。在Java代码中,可以通过以下方式获取这些属性:

SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(configuration);
Properties properties = sessionFactory.getConfiguration().getVariables();
String username = properties.getProperty("username");
String password = properties.getProperty("password");

propertiesElement 就是将配置文件转换成Properties对象,全局可以通过 properties.getProperty 方法获取属性。

3、loadCustomVfs(settings)

接下来我们看这段

loadCustomVfs(settings);

loadCustomVfs 是 MyBatis中XMLConfigBuilder类中的一个私有方法,用于加载自定义的虚拟文件系统(VFS)。下面是对该方法的详细解释:

private void loadCustomVfs(Properties props) throws ClassNotFoundException {
    // 获取vfsImpl属性的值
    String value = props.getProperty("vfsImpl");
    if (value != null) {
        // 将vfsImpl属性的值按照逗号分隔符拆分成多个类名
        String[] clazzes = value.split(",");
        for (String clazz : clazzes) {
            if (!clazz.isEmpty()) {
                // 加载指定的类
                @SuppressWarnings("unchecked")
                Class<? extends VFS> vfsImpl = (Class<? extends VFS>) Resources.classForName(clazz);
                // 注册自定义的VFS实现类
                configuration.setVfsImpl(vfsImpl);
            }
        }
    }
}

该方法首先从配置文件中获取vfsImpl属性的值,该属性指定了自定义的VFS实现类。如果该属性存在,则将其按照逗号分隔符拆分成多个类名,并逐个加载指定的类。最后,该方法将自定义的VFS实现类注册到Configuration对象中,以供后续的数据库操作使用。

举个例子,假设我们需要使用自定义的VFS实现类来管理MyBatis的映射文件,我们可以在MyBatis的配置文件中添加如下配置:

<configuration>
  <properties>
    <property name="vfsImpl" value="com.example.MyVFS" />
  </properties>
  ...
</configuration>

其中,vfsImpl属性指定了自定义的VFS实现类为com.example.MyVFS。在Java代码中,可以通过以下方式加载自定义的VFS实现类:

SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(configuration);
VFS myVFS = new MyVFS();
configuration.setVfsImpl(myVFS);

以上代码中,我们通过SqlSessionFactoryBuilder创建了SqlSessionFactory对象,并创建了自定义的VFS实现类MyVFS。最后,我们通过setVfsImpl方法将自定义的VFS实现类注册到Configuration对象中。

4、loadCustomLogImpl(settings)

接下来我们看这段

loadCustomLogImpl(settings);

loadCustomLogImpl(settings)是MyBatis框架中XMLConfigBuilder类的一个私有方法,用于加载自定义的日志实现类。下面是对该方法的详细解释:

private void loadCustomLogImpl(Properties props) {
    Class<? extends Log> clazz = resolveClass(props.getProperty("logImpl"));
    configuration.setLogImpl(clazz);
}

该方法首先通过getProperty方法获取配置文件中名为logImpl的属性值,该属性值指定了自定义的日志实现类。接着,通过resolveClass方法将属性值转换为Class对象,最后将Class对象设置到Configuration对象中,以供后续的数据库操作使用。

举个例子,假设我们需要使用自定义的日志实现类来记录MyBatis的日志信息,我们可以在MyBatis的配置文件中添加如下配置:

<configuration>
  <properties>
    <property name="logImpl" value="com.example.MyLog" />
  </properties>
  ...
</configuration>

其中,logImpl属性指定了自定义的日志实现类为com.example.MyLog。在Java代码中,可以通过以下方式加载自定义的日志实现类:

SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(configuration);
Log myLog = new MyLog();
configuration.setLogImpl(myLog.getClass());

以上代码中,我们通过SqlSessionFactoryBuilder创建了SqlSessionFactory对象,并创建了自定义的日志实现类MyLog。最后,我们通过setLogImpl方法将自定义的日志实现类注册到Configuration对象中。

5、typeAliasesElement(context.evalNode(“typeAliases”))

接下来我们看这段

typeAliasesElement(context.evalNode("typeAliases")

typeAliasesElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 typeAliases 元素。下面是该方法的源代码及其说明:

public Element typeAliasesElement(XNode parent) {
  if (parent != null) {
    // 获取typeAliases元素
    for (XNode child : parent.getChildren()) {
      if ("typeAliases".equals(child.getName())) {
        return (Element) child.getNode();
      }
    }
  }
  return null;
}

该方法接收一个 XNode 类型的参数 parent ,表示要解析的MyBatis配置文件中的某个节点。该方法首先判断该节点是否为 typeAliases ,如果是,则返回该节点的 Element 对象,否则返回 null 。

下面是一个示例代码,它演示了如何使用 typeAliasesElement 方法来获取MyBatis配置文件中的 typeAliases 元素:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLConfigBuilder builder = new XMLConfigBuilder(inputStream);
Configuration configuration = builder.parse();
XNode typeAliasesNode = configuration.getTypeAliasesElement();
if (typeAliasesNode != null) {
  Element typeAliasesElement = typeAliasesNode.evalNode("typeAliases");
  // 处理typeAliasesElement
}

在这个示例代码中,我们首先创建一个 XMLConfigBuilder 对象,并使用它解析MyBatis配置文件。然后,我们获取配置文件中的 typeAliases 节点,并使用 typeAliasesElement 方法获取 typeAliases 元素。如果 typeAliases 元素存在,则可以对其进行进一步的处理。

6、pluginElement(context.evalNode(“plugins”))

pluginElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 plugins 元素。下面是该方法的源代码及其说明:

public void pluginElement(XNode parent) throws Exception {
  if (parent != null) {
    // 获取plugins元素
    for (XNode child : parent.getChildren()) {
      // 获取plugin元素
      if ("plugin".equals(child.getName())) {
        // 获取plugin元素的属性
        String interceptor = child.getStringAttribute("interceptor");
        Properties properties = child.getChildrenAsProperties();
        // 创建Interceptor实例并设置属性
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        interceptorInstance.setProperties(properties);
        // 添加到InterceptorChain中
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }
}

该方法接收一个 XNode 类型的参数 parent ,表示要解析的MyBatis配置文件中的某个节点。该方法首先判断该节点是否为 plugins ,如果是,则遍历其所有子节点,查找名为 plugin 的子节点。对于每个 plugin 子节点,该方法获取其 interceptor 属性和所有子节点的属性,并创建对应的 Interceptor 实例,并将其添加到 InterceptorChain 中。

下面是一个示例代码,它演示了如何使用 pluginElement 方法来解析MyBatis配置文件中的 plugins 元素:

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLConfigBuilder builder = new XMLConfigBuilder(inputStream);
Configuration configuration = builder.parse();
XNode pluginsNode = configuration.getPluginsElement();
if (pluginsNode != null) {
  pluginElement(pluginsNode);
}

在这个示例代码中,我们首先创建一个 XMLConfigBuilder 对象,并使用它解析MyBatis配置文件。然后,我们获取配置文件中的 plugins 节点,并使用 pluginElement 方法解析其中的 plugin 元素。

7、 objectFactoryElement(context.evalNode(“objectFactory”))

objectFactoryElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 objectFactory 元素。下面是该方法的源代码及其说明:

public void objectFactoryElement(XNode context) throws Exception {
  if (context != null) {
    // 获取objectFactory元素的type属性和子元素
    String type = context.getStringAttribute("type");
    List<ConstructorArg> constructorArgs = parseConstructorArgElements(context.getChildren());
    // 创建ObjectFactory实例并设置属性
    ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();
    if (constructorArgs.size() > 0) {
      // 通过构造方法创建实例
      factory = (ObjectFactory) createInstanceByConstructor(factory, constructorArgs);
    }
    factory.setProperties(context.getChildrenAsProperties());
    // 设置ObjectFactory
    configuration.setObjectFactory(factory);
  }
}

该方法接收一个 XNode 类型的参数 context ,表示要解析的MyBatis配置文件中的某个节点。该方法首先判断该节点是否为 objectFactory ,如果是,则获取其 type 属性和所有子元素,并根据 type 属性创建对应的 ObjectFactory 实例,并设置其属性。如果存在构造方法参数,则使用这些参数创建实例。最后,将创建的 ObjectFactory 实例设置到 Configuration 中。

8、objectWrapperFactoryElement(context.evalNode(“objectWrapperFactory”))

objectWrapperFactoryElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 objectWrapperFactory 元素。下面是该方法的源代码及其说明:

public void objectWrapperFactoryElement(XNode context) throws Exception {
  if (context != null) {
    // 获取objectWrapperFactory元素的type属性
    String type = context.getStringAttribute("type");
    // 创建ObjectWrapperFactory实例并设置属性
    ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();
    configuration.setObjectWrapperFactory(factory);
  }
}

该方法接收一个 XNode 类型的参数 context ,表示要解析的MyBatis配置文件中的某个节点。该方法首先判断该节点是否为 objectWrapperFactory ,如果是,则获取其 type 属性,并根据 type 属性创建对应的 ObjectWrapperFactory 实例,并设置其属性。最后,将创建的 ObjectWrapperFactory 实例设置到 Configuration 中。

9、reflectorFactoryElement(context.evalNode(“reflectorFactory”))

reflectorFactoryElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 reflectorFactory 元素。下面是该方法的源代码及其说明:

public void reflectorFactoryElement(XNode context) throws Exception {
  if (context != null) {
    // 获取reflectorFactory元素的type属性
    String type = context.getStringAttribute("type");
    // 创建ReflectorFactory实例并设置属性
    ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();
    configuration.setReflectorFactory(factory);
  }
}

该方法接收一个 XNode 类型的参数 context ,表示要解析的MyBatis配置文件中的某个节点。该方法首先判断该节点是否为 reflectorFactory ,如果是,则获取其 type 属性,并根据 type 属性创建对应的 ReflectorFactory 实例,并设置其属性。最后,将创建的 ReflectorFactory 实例设置到 Configuration 中。

10、settingsElement(settings)

settingsElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 settings 元素。下面是该方法的源代码及其说明:

public void settingsElement(Properties props) {
  // 将Properties对象中的所有属性设置到Configuration中
  configuration.setVariables(props);
  // 设置cacheEnabled属性
  if (props.containsKey("cacheEnabled")) {
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
  }
  // 设置lazyLoadingEnabled属性
  if (props.containsKey("lazyLoadingEnabled")) {
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
  }
  // 设置aggressiveLazyLoading属性
  if (props.containsKey("aggressiveLazyLoading")) {
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
  }
  // 设置multipleResultSetsEnabled属性
  if (props.containsKey("multipleResultSetsEnabled")) {
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
  }
  // 设置useColumnLabel属性
  if (props.containsKey("useColumnLabel")) {
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
  }
  // 设置useGeneratedKeys属性
  if (props.containsKey("useGeneratedKeys")) {
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
  }
  // 设置autoMappingBehavior属性
  if (props.containsKey("autoMappingBehavior")) {
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior")));
  }
  // 设置defaultExecutorType属性
  if (props.containsKey("defaultExecutorType")) {
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType")));
  }
  // 设置defaultStatementTimeout属性
  if (props.containsKey("defaultStatementTimeout")) {
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
  }
  // 设置defaultFetchSize属性
  if (props.containsKey("defaultFetchSize")) {
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
  }
  // 设置safeRowBoundsEnabled属性
  if (props.containsKey("safeRowBoundsEnabled")) {
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
  }
  // 设置mapUnderscoreToCamelCase属性
  if (props.containsKey("mapUnderscoreToCamelCase")) {
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
  }
  // 设置localCacheScope属性
  if (props.containsKey("localCacheScope")) {
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope")));
  }
  // 设置jdbcTypeForNull属性
  if (props.containsKey("jdbcTypeForNull")) {
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull")));
  }
  // 设置lazyLoadTriggerMethods属性
  if (props.containsKey("lazyLoadTriggerMethods")) {
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
  }
}

该方法接收一个 Properties 类型的参数 props ,表示要解析的MyBatis配置文件中的 settings 元素。该方法首先将 Properties 对象中的所有属性设置到 Configuration 中,然后根据属性名设置对应的属性值。

11、environmentsElement(context.evalNode(“environments”))

environmentsElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 environments 元素。下面是该方法的源代码及其说明:

public void environmentsElement(XNode context) throws Exception {
  if (context != null) {
    // 如果environment属性没有设置,就使用default属性指定的环境
    if (environment == null) {
      environment = context.getStringAttribute("default");
    }
    // 遍历所有的environment子元素,找到指定的环境
    for (XNode child : context.getChildren()) {
      String id = child.getStringAttribute("id");
      if (isSpecifiedEnvironment(id)) {
        // 解析transactionManager和dataSource元素,并设置到Configuration对象中
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
        DataSource dataSource = dsFactory.getDataSource();
        Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
        configuration.setEnvironment(environmentBuilder.build());
      }
    }
  }
}

该方法接收一个 XNode 类型的参数 context ,表示要解析的MyBatis配置文件中的 environments 元素。该方法首先检查 environment 属性是否设置,如果没有设置,则使用 default 属性指定的环境。然后,遍历所有的 environment 子元素,找到指定的环境,并解析其中的 transactionManager 和 dataSource 元素,并将它们设置到 Configuration 对象中。

12、 databaseIdProviderElement(context.evalNode(“databaseIdProvider”))

databaseIdProviderElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 databaseIdProvider 元素。下面是该方法的源代码及其说明:

private void databaseIdProviderElement(XNode context) throws Exception {
  if (context != null) {
    String type = context.getStringAttribute("type");
    if ("VENDOR".equals(type)) {
      type = "DB_VENDOR";
    }
    Properties properties = context.getChildrenAsProperties();
    DatabaseIdProvider databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
    databaseIdProvider.setProperties(properties);
    // 获取所有已经加载的DataSourceFactory
    Set<String> dataSourceIds = new HashSet<>();
    for (String name : configuration.getDataSourceNames()) {
      dataSourceIds.add(name);
      DataSource dataSource = getDataSourceFromEnvironment(name);
      MetaObject metaDataSource = configuration.newMetaObject(dataSource);
      // 获取databaseId
      String databaseId = databaseIdProvider.getDatabaseId(dataSource);
      // 设置databaseId
      metaDataSource.setValue("databaseId", databaseId);
    }
    // 处理未加载的DataSourceFactory
    for (String id : configuration.getConfigurationProperties().<String>getSet(DataSourceFactory.DataSourceFactoryProperty.DATA_SOURCE_FACTORY)) {
      if (!dataSourceIds.contains(id)) {
        DataSourceFactory factory = resolveDataSourceFactory(id);
        DataSource dataSource = factory.getDataSource();
        MetaObject metaDataSource = configuration.newMetaObject(dataSource);
        String databaseId = databaseIdProvider.getDatabaseId(dataSource);
        metaDataSource.setValue("databaseId", databaseId);
      }
    }
  }
}

该方法接收一个 XNode 类型的参数 context ,表示要解析的MyBatis配置文件中的 databaseIdProvider 元素。该方法首先获取 type 属性的值,然后使用反射机制创建对应的 DatabaseIdProvider 对象,并将 context 中的子元素转换为 Properties 对象,最后将这些属性设置到 DatabaseIdProvider 对象中。接着,该方法获取所有已经加载的 DataSourceFactory ,遍历它们并获取 databaseId ,并将其设置到对应的 DataSource 对象中。最后,该方法处理未加载的 DataSourceFactory ,并为它们设置 databaseId

13、 typeHandlerElement(context.evalNode(“typeHandlers”))

typeHandlerElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 typeHandlers 元素。下面是该方法的源代码及其说明:

private void typeHandlerElement(XNode parent) {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        String typeHandlerPackage = child.getStringAttribute("name");
        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);
        }
      }
    }
  }
}

该方法接收一个 XNode 类型的参数 parent ,表示要解析的MyBatis配置文件中的 typeHandlers 元素。该方法首先遍历 parent 的所有子元素,如果子元素的名称为 package ,则获取 name 属性的值,并将其注册到 typeHandlerRegistry 中。如果子元素的名称不为 package ,则获取 javaType 、 jdbcType 和 handler 属性的值,并使用反射机制创建对应的 TypeHandler 对象,并将其注册到 typeHandlerRegistry 中。
下面是一个示例代码,它演示了如何使用 typeHandlerElement 方法来解析MyBatis配置文件中的 typeHandlers 元素:

14、mapperElement(context.evalNode(“mappers”))

mapperElement 方法是MyBatis中的一个工具方法,用于解析MyBatis配置文件中的 mappers 元素。下面是该方法的源代码及其说明:

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");
        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) {
          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<?> 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.");
        }
      }
    }
  }
}

该方法接收一个 XNode 类型的参数 parent ,表示要解析的MyBatis配置文件中的 mappers 元素。该方法首先遍历 parent 的所有子元素,如果子元素的名称为 package ,则获取 name 属性的值,并将其添加到 Configuration 对象中的MapperRegistry中。如果子元素的名称不为 package ,则获取 resource 、 url 和 class 属性的值,并根据不同情况进行解析和处理。

在这里插入图片描述

💕💕 本文由激流原创,首发于CSDN博客,博客主页 https://blog.csdn.net/qq_37967783?spm=1010.2135.3001.5421
💕💕喜欢的话记得点赞收藏啊

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/649241.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Hadoop(CentOS)安装及MapReduce实现数据去重

Hadoop&#xff08;CentOS&#xff09;安装及MapReduce实现数据去重 1. JDK安装 1.1 资源下载&#xff1a; 下载地址&#xff1a;https://pan.quark.cn/s/17d7266205f9 hadoop的安装 包 java jdk安装包 eclipse连接hadoop工具 所学其他工具可自行在官网下载 centos下载地…

Verilog 高级知识点

目录 Verilog 高级知识点 1、阻塞赋值&#xff08;Blocking&#xff09; 2、非阻塞赋值&#xff08;Non-Blocking&#xff09; 3 、assign 和 always 区别 4、什么是 latch Verilog 高级知识点 本节给大家介绍一些高级的知识点。高级知识点包括阻塞赋值和非阻塞赋值、assi…

合宙Air724UG Cat.1模块硬件设计指南--ADC接口

ADC接口 简介 ADC(Analog-to-Digital Converter) 指模数转换器。是指将连续变化的模拟信号转换为离散的数字信号的器件。 合宙Cat.1模块ADC接口主要用来检测模拟电压信号量&#xff0c;用于电池电压检测&#xff0c;温湿度检测&#xff0c;TDS检测等应用。 特性 ADC精度&#…

华为OD机试真题 JavaScript 实现【素数之积】【2022Q4 100分】

一、题目描述 RSA加密算法在网络安全世界中无处不在&#xff0c;它利用了极大整数因数分解的困难度&#xff0c;数据越大&#xff0c;安全系数越高。 给定一个32位正整数&#xff0c;请对其进行因数分解&#xff0c;找出是哪两个素数的乘积。 二、输入描述 一个正整数num …

【自动文摘】BART实现finetune及evaluate

文章目录 BART介绍下游任务 fine tune BART介绍 我最近在看自动文摘相关的预训练模型&#xff0c;BART模型是2019-2020左右提出来的&#xff0c;首先介绍下它的基本情况。 论文&#xff1a;https://arxiv.org/abs/1910.13461 github&#xff1a; fairseq例子&#xff1a;http…

Efficient Global 2D-3D Matching for Camera Localization in a Large-Scale 3D Map

文章目录 Efficient Global 2D-3D Matching for Camera Localization in a Large-Scale 3D Map1. 相似源码choose_solution.pyeight_point.pyepipolar_match.py Efficient Global 2D-3D Matching for Camera Localization in a Large-Scale 3D Map 1. 相似源码 由于paper并没…

ChatGPT带火的提示工程师,构造自己的提示语

ChatGPT是一个大语言模型&#xff0c;学过全球几乎公开的大部分有效知识库&#xff0c;它什么都懂。 ChatGPT的风靡&#xff0c;爆火了一个年薪百万的提示工程师这个新就业岗位。 提示工程师&#xff0c;也就是AI训练师&#xff0c;即通过与AI交互写出相关提示&#xff0c;以帮…

Spring Boot进阶(48):SpringBoot之集成WebSocket及使用说明 | 超级详细,建议收藏

1. 前言&#x1f525; 对于很多小伙伴来说&#xff0c;项目现在都普遍前后端分离&#xff0c;各干各的事儿&#xff0c;在前后端进行服务调用才会有大面积的碰头&#xff0c;后端接口数据格式发生变更要通知前端&#xff0c;相反&#xff0c;前端有变化要告诉后端&#xff0c;这…

【这小文章绝了!】一文看穿,MATLAB | 数组与矩阵超详细入门进阶必须看

目录 介绍 一、数组的创建和操作 通过 : 创建一维数组 通过logspace函数创建一维数组 通过linspace函数创建一维数组 二、数组的运算 数组的关系运算 数组的逻辑运算 三、矩阵 矩阵的构造 矩阵的下标引用 矩阵大小 四、矩阵元素的运算 矩阵的差分 矩阵运算 矩阵…

Spring Boot进阶(50):Spring Boot如何全局统一处理异常?| 超级详细,建议收藏

1. 前言&#x1f525; 今天和大家讨论的是Spring Boot如何统一处理异常。这里先说一下我们为什么需要全局统一处理异常&#xff1f;其实理由很简单&#xff0c;因为程序在运行的过程中&#xff0c;不可避免会产生各种各样的错误。比如说用户传过来的参数不正确&#xff0c;无法…

4端到端协议-4.2【实验】【计算机网络】

4端到端协议-4.2【实验】【计算机网络】 前言推荐4端到端协议4.2 TCP协议流捕获与TCP协议分析实验目的实验内容及实验环境实验原理实验过程实验过程演示 4.2.1实验章节测验一.单选题&#xff08;共5题,25.0分&#xff09;二.阅读理解&#xff08;共1题,70.0分&#xff09;三.填…

xxl-job核心源码解析

xxl-job源码解析 如何自研一个xxljob 注册服务调度服务RPC组件(基建&#xff0c;底层严重依赖)日志服务告警服务 系统架构 执行流程 各大调度中心对比 1&#xff09;服务端启动流程 首先找到配置类 XxlJobAdminConfig 可以发现该类实现 InitializingBean接口&#xff0c;…

openpose保姆级教程代码详细解析——训练部分

一&#xff1a;前言 OpenPose是一个基于深度学习的人体姿势估计库&#xff0c;它可以从图像或视频中准确地检测和估计人体的关键点和姿势信息。OpenPose的目标是将人体姿势估计变成一个实时、多人、准确的任务。它的原理部分已经在上一篇非常详细的讲解了——本节介绍训…

Dynamic .NET TWAIN 8.3.3 for NetCore Crack

用于 WinForms 和 WPF 应用程序的扫描仪和网络摄像头捕获 SDK 适用于 .NET 应用程序的简单高效的扫描和网络摄像头 SDK Dynamsoft 的 Dynamic .NET TWAIN 是一个基于 TWAIN 和 DirectShow 标准的 .NET 文档成像 SDK。它为扫描仪和网络摄像头软件开发提供了丰富、高效且…

修改依赖包下的子依赖版本,前端项目安全扫描出来的漏洞——解决过程

为什么要升级&#xff0c;如图云桌面&#xff08;相当于堡垒机-远程桌面&#xff09;的项目审查是大概基于node16版本进行扫描的&#xff0c;本来我方是通过降版本从14到12绕过大范围更新&#xff0c;但现在躲得过初一躲不过十五&#xff0c;如何更新 package-lock.json 中的一…

DAY24:二叉树(十四)二叉搜索树中的插入操作+删除二叉搜索树中的节点(二叉树结构修改难点)

文章目录 701.二叉搜索树中的插入操作思路递归法如何保证连接的节点就是空节点的父节点&#xff1f; 迭代法迭代法注意debug测试 450.删除二叉搜索树中的节点&#xff08;坑较多&#xff0c;注意复盘&#xff09;思路最开始的写法debug测试1.使用了释放后的空间ERROR: AddressS…

通知!2023年湖北住建厅七大员新考和继续教育要求有变化了?启程别

通知&#xff01;2023年湖北住建厅七大员新考和继续教育要求有变化了&#xff1f;启程别 湖北住建厅七大员新考以及继续教育的相关要求都即将有一些变化了目前在征集意见的阶段&#xff0c;具体实施等后续具体通知 对于新考的变化主要是&#xff1a; 1.由原先报名之后只需要完成…

Vue中如何进行颜色选择与调色板

Vue中如何进行颜色选择与调色板 颜色选择和调色板是Web开发中常用的功能&#xff0c;它们可以帮助用户选择或调整颜色。Vue作为一个流行的JavaScript框架&#xff0c;提供了一些工具和库&#xff0c;可以方便地实现颜色选择和调色板功能。本文将介绍如何在Vue中进行颜色选择和…

【aspose-words】Aspose.Words for Java模板语法详细剖析

文章目录 前言&#x1f34a;缘由aspose-words模板语法再了解 &#x1f3af;主要目标实现3大重点 &#x1f381;快速链接&#x1f348;猜你想问如何与狗哥联系进行探讨1.关注公众号【JavaDog程序狗】2.踩踩狗哥博客 &#x1f36f;猜你喜欢文章推荐 正文&#x1f34b;aspose-word…

mfc140.dll丢失的解决方法,解析mfc140.dll这个文件

其实大部分人在使用计算机过程中&#xff0c;您可能会遇到mfc140.dll丢失的错误提示。这个错误会导致一些应用程序无法正常运行&#xff0c;那么要如何解决这个问题呢&#xff1f;今天小编就来给大家详细的解析mfc140.dll这个文件以及教大家 mfc140.dll丢失的解决方法。 目录 …