Mybatis配置之解析typeAliases底层源码

主要记录自己看Mybatis源码的过程,写的不是很好只能自己看懂,见谅。

如下typeAliases解析主要是靠XMLConfigBuilder这个类。

<?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>
    <typeAliases>
        <package name="com.danielei.entity"></package>
    </typeAliases>
   省略其他。。
</configuration>

org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
此方法解析mybatis-config.xml中节点名称为configuration的标签

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

追踪parseConfiguration方法
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
此方法 解析一大推标签,我此时只看 typeAliasesElement(root.evalNode(“typeAliases”));这行

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

追踪typeAliasesElement这个方法

这个方法主要干的就是循环typeAliases的子节点,如果节点的名称等于"package"

        //找到这行配置
        <package name="com.danielei.entity"></package>

org.apache.ibatis.builder.xml.XMLConfigBuilder#typeAliasesElement

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        //true 进入此分支 
        if ("package".equals(child.getName())) {
        // 获取package节点的name属性
        //typeAliasPackage  =“com.danielei.entity”
          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);
          }
        }
      }
    }
  }

org.apache.ibatis.type.TypeAliasRegistry#registerAliases(java.lang.String)
//跟过来如下代码

public void registerAliases(String packageName){
//继续跟踪
    registerAliases(packageName, Object.class);
  }

org.apache.ibatis.type.TypeAliasRegistry#registerAliases(java.lang.String, java.lang.Class<?>)
继续跟踪 代码如下

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

org.apache.ibatis.io.ResolverUtil#find
代码如下

 - public ResolverUtil<T> find(Test test, String packageName) {    //
   把包名中的.转换为/ 就是下面这行代码。    //return packageName == null ? null :
   packageName.replace('.', '/');
       String path = getPackagePath(packageName);
       try {
       //这行代码获取到 包下的文件路径,包名+XXX.class ,包名+User.class 等等文件
         List<String> children = VFS.getInstance().list(path);
         for (String child : children) {
           if (child.endsWith(".class")) {
            //跟踪一下这个方法
             addIfMatching(test, child);
           }
         }
       } catch (IOException ioe) {
         log.error("Could not read package: " + packageName, ioe);
       }
       return this;   }

org.apache.ibatis.io.ResolverUtil#addIfMatching
代码如下

protected void addIfMatching(Test test, String fqn) {
    try {
      //截取字串 = .class不要,并且把 /替换成. 得出 com.danielei.entity.UserEntity
      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)) {
        //放入Set集合中 方法跑完了,回到主线方法
        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());
    }
  }

org.apache.ibatis.type.TypeAliasRegistry#registerAliases(java.lang.String, java.lang.Class<?>)
回到此方法

public void registerAliases(String packageName, Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    //此时执行这行代码 获取刚才存实体的Set集合数据。
    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);
      }
    }
  }

org.apache.ibatis.type.TypeAliasRegistry#registerAlias(java.lang.Class<?>)
跟踪此方法

 public void registerAlias(Class<?> type) {
  //这里获取 类名=UserEntity
    String alias = type.getSimpleName();
    //获取类上的注解 @Alias("自定义名称") 
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    //有设置注解取注解的自定义名称,没设置该注解默认是 类名
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    } 
    //跟踪此方法
    registerAlias(alias, type);
  }

如图自定义别名
如图自定义别名

org.apache.ibatis.type.TypeAliasRegistry#registerAlias(java.lang.String, java.lang.Class<?>)

public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    //把类名转为小写当作Map中的key。
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    //最重要的代码,扫描的实体会存放在这个Map中。 key为小写类名,value是实例 
    TYPE_ALIASES.put(key, value);
  }

到这里typeAliases核心代码就是这样了。
总结:mybatis-config.xml配置typeAliases标签 解析主要是XML技术
先解析父节点 <configuration> 然后解析<typeAliases>标签
再解析<typeAliases>标签下的子标签<package name="com.danielei.entity"></package>
然后获取 name属性,获取到这个包路径后,去找到改路径下的文件,把.class类文件结尾的拿到存入List中,此时是多个包名+文件名组成的字符串,然后循环实例化并存入Set集合中。 再用Set集合循环 存入Map中 key为(小写的)类名,value就是类实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值