MyBatis 3.5.4源码之旅二之初始化流程二

本文深入探讨MyBatis3.5.4版本的初始化流程,包括配置文件解析、设置配置解析、别名解析等关键步骤,为理解MyBatis内部工作原理提供详尽指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对照的流程图

在这里插入图片描述

XMLConfigBuilder的parse

接下去就是最重要的build(parser.parse())了,我们先看parser.parse()

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

XMLConfigBuilder的parseConfiguration解析配置文件

一看就知道要开始解析xml了,从configuration节点开始。主要的解析流程就在这里了:

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

XMLConfigBuilder的settingsAsProperties解析设置配置

有那么多要解析和处理的,我就不全讲了,讲几个我们经常用到的,比如解析settings,我们要设置二级缓存settingsAsProperties

  private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    //获取属性 比如lazyLoadingEnabled 
    Properties props = context.getChildrenAsProperties();
    // 检查是否是合法的,就是能识别的,用了mybatis不能识别的就报错啦
    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;
  }

XMLConfigBuilder的typeAliasesElement解析别名

再来看看别名解析typeAliasesElement,分package和另外一种,但是注册时候是一样的,package只是还需要解析出包下的类的Class对象:

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

其中主要是configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);

  public void registerAliases(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    //获取包下的所有类的Class对象
    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(new ResolverUtil.IsA(superType), packageName);

 public ResolverUtil<T> find(Test test, String packageName) {
    String path = getPackagePath(packageName);

    try {
    //用类加载器去获取包下字节码文件路径名 com/xxx/xx/xx.class
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
        //从路径名解析出对应的Class对象
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }

    return this;
  }

在这里插入图片描述
解析出对应的Class对象addIfMatching放入HashSet中:

protected void addIfMatching(Test test, String fqn) {
    try {
      String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
      ClassLoader loader = getClassLoader();
      if (log.isDebugEnabled()) {
        log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
      }

      Class<?> type = loader.loadClass(externalName);
      if (test.matches(type)) {
        matches.add((Class<T>) type);
      }
    } catch (Throwable t) {
      log.warn("Could not examine class '" + fqn + "'" + " due to a " +
          t.getClass().getName() + " with message: " + t.getMessage());
    }
  }

然后取出来所有对象进行注册:
在这里插入图片描述
获取简单类名,就是没有包含包名的,注册过程中发现有注解别名的,优先使用注解的别名:
在这里插入图片描述
最后把别名小写当做keyClass对象当做Value注册进去:
在这里插入图片描述
本来还想讲mapperElement(root.evalNode("mappers"));,但是这个比较复杂,篇幅就巨长了,所以打算留着下次讲。

总结下,初始化做的大致事情:

  1. 加载配置文件
  2. 创建SqlSessionFactory
  3. 创建XMLConfigBuilder
  4. 解析配置文件

当然具体的肯定会非常细,这个我觉得我也不可能带着全部走完,还是需要出现问题,或者有时间可以debug跟进去看看,了解下源码的大致脉络。

好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值