加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

mybatis源码配置文件解析之三:解析typeAliases标签

发布时间:2020-12-14 18:03:25 所属栏目:大数据 来源:网络整理
导读:在前边的博客在分析了mybatis解析settings标签,《mybatis源码配置文件解析之二:解析settings标签》。下面来看解析typeAliases标签的过程。 一、概述 在mybatis核心配置文件(mybatis-config.xml)中有关typeAliases的配置如下, typeAliases package name=

在前边的博客在分析了mybatis解析settings标签,《mybatis源码配置文件解析之二:解析settings标签》。下面来看解析typeAliases标签的过程。

一、概述

在mybatis核心配置文件(mybatis-config.xml)中有关typeAliases的配置如下,

<typeAliases>
       <package name="cn.com.mybatis.bean"></package>
       <typeAlias name="user" type="cn.com.mybatis.bean.User"></typeAlias>
</typeAliases>

上面给出了两种配置typeAlias的放式,一种是配置package标签,一种是typeAlias表。

我上面的配置是有问题的,在测试的时候一直报下面的错误,

上面的问题困扰了笔者好久,没找到原因,因为解析typeAliases标签的源码中找不到任何的原因,最后排查日志,原来是在加载核心配置文件的时候要把配置和mybatis的dtd文件进行验证,这里是验证出错了,具体的错误是typeAlias标签必须在package标签的前边,也就是标签是有顺序的。把配置改为下面的顺序,程序正常,

<typeAliases>
       <typeAlias alias="user" type="cn.com.mybatis.bean.User"></typeAlias>
       <package name="cn.com.mybatis.bean"/>
    </typeAliases>

?

1、配置<package>标签

<package>标签配置的是一个包名,mybatis会扫描该包下的所有类,并注册一个别名,这里在标签中无法为某个类指定一个自定义的别名,mybatis提供了另外一种方式可以使用自定义的别名,即@Alias注解,在类上标记该注解,如下,

package cn.com.mybatis.bean;

import org.apache.ibatis.type.Alias;

//配置别名为myMenu
@Alias(value="myMenu")
public class Menu {

    private String menuId;
     String menuName;
     String url;
}

上面为Menu类配置了别名,在扫描该包的时候会使用自定义的别名,不会使用mybatis默认的别名规则(Class.getSimpleName())

2、配置<typeAlias>标签

这种配置是单独为某个类配置别名,其中alias属性可以不配置,不配置则使用mybatis默认的别名规则,如下

<typeAlias alias="MyUser" type="cn.com.mybatis.bean.User"></typeAlias>

?

上面看了typeAlias的两种配置方式,那么何为typeAlias,意思就是给一个类配置一个别名,如这里有一个cn.com.mybatis.bean.User类,可以为其配置别名为MyUser,

那么在配置文件中便可以使用别名代替类的全限类名,目的是简便。这里需要注意的是配置的别名的使用范围仅限于mybatis的配置文件中(包含核心配置文件和Mpper映射文件)

二、详述

上面,了解了typeAlias的配置及作用,下面看mybatis是如何解析的。

在XMLConfigBuilder类中的parseConfiguration方法,

  private void parseConfiguration(XNode root) {
    try {
      issue #117 read properties first
      解析properties标签    
      propertiesElement(root.evalNode("properties"));
      解析settings标签,1、把<setting>标签解析为Properties对象
      Properties settings = settingsAsProperties(root.evalNode("settings"/*2、对<settings>标签中的<setting>标签中的内容进行解析,这里解析的是<setting name="vfsImpl" value=",">
      * VFS是mybatis中用来表示虚拟文件系统的一个抽象类,用来查找指定路径下的资源。上面的key为vfsImpl的value可以是VFS的具体实现,必须
      * 是权限类名,多个使用逗号隔开,如果存在则设置到configuration中的vfsImpl属性中,如果存在多个,则设置到configuration中的仅是最后一个
      * */
      loadCustomVfs(settings);
      解析别名标签,例<typeAlias alias="user" type="cn.com.bean.User"/>
      typeAliasesElement(root.evalNode("typeAliases"解析插件标签
      pluginElement(root.evalNode("plugins"解析objectFactory标签,此标签的作用是mybatis每次创建结果对象的新实例时都会使用ObjectFactory,如果不设置
      则默认使用DefaultObjectFactory来创建,设置之后使用设置的
      objectFactoryElement(root.evalNode("objectFactory"解析objectWrapperFactory标签
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"解析reflectorFactory标签
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
       read it after objectFactory and objectWrapperFactory issue #631
      解析environments标签
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"解析<mappers>标签
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e,e);
    }
  }

从上面可以看出typeAliasesElement方法,此方法用来解析typeAliases标签及其子标签,

 typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
          1、解析package标签
        if ("package".equals(child.getName())) {
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
            2、解析typeAlias标签
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
           {
            Class<?> clazz = Resources.classForName(type);
            if (alias == ) {
              typeAliasRegistry.registerAlias(clazz);
            }  {
              typeAliasRegistry.registerAlias(alias,clazz);
            }
          }  (ClassNotFoundException e) {
            new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " +
);
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        }

从上面可以看到获取<package>标签的name属性,也就配置的包名,然后调用下面的方法,

configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);

可以看到从Configuration中获得TypeAliasRegistry,然后调用其registerAliases方法,

 registerAliases(String packageName){
    registerAliases(packageName,Object.);
  }

又调用另外一个方法,如下,

/**
   * 
   * 为包下的所有java bean注册别名
   * @param packageName
   *  superType
   */
  void registerAliases(String packageName,Class<?> superType){
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
    把该包下的所有类进行加载,把其Class对象放到resolverUtil的matches中
    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中的matches中,这里具体的遍历方法暂且不看。遍历完成后取出resolverUtil中的所有Class对象,只要不是匿名类、接口则执行registerAlias方法,

void registerAlias(Class<?> type) {
      获得类的简单类名,如cn.com.mybatis.bean.User 则其简单名称为User
    String alias = type.getSimpleName();
    判断类上是否存在@Alias注解
    Alias aliasAnnotation = type.getAnnotation(Alias.);
    如果存在@Alias注解,则使用注解上配置的value属性作为别名
    if (aliasAnnotation != ) {
      alias = aliasAnnotation.value();
    } 
    registerAlias(alias,type);
  }

看上面的方法,上面的方法先获得Class的简单类名,

获得类的简单类名,如cn.com.mybatis.bean.User 则其简单名称为User
    String alias = type.getSimpleName();

然后会判断类上是否有@Alias注解,如果有则取其value值作为类的别名,

 aliasAnnotation.value();
    } 

进行上面的判断,存在@Alias注解,使用其value值作为别名,否则使用类的简单类名(Class.getSimpleName()),然后执行registerAlias方法,

void registerAlias(String alias,1)"> value) {
    new TypeException("The parameter alias cannot be null");
    }
     issue #748
    String key = alias.toLowerCase(Locale.ENGLISH);
    如果已经注册了改别名则会抛异常
    if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
      new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
    }
    TYPE_ALIASES.put(key,value);
  }

上面的代码会把别名转化为英文的小写作为存入的key,使用对应的Class存入TYPE_ALIASES中。如果已经注册过该key则会抛出异常,也就是不允许重复注册或者相同的key是无法覆盖的。这里还有一个问题,如果我们配置的是别名中含有大写,那么注册的时候是小写的,在使用的时候是用配置的还是用注册的,例,上面的例子,

 String url;
}

这里配置的是myMenu,注册的确实下面的

可以看到注册之后的是mymenu。其实在使用的时候是大小写不敏感的,在匹配的时候会统一转化为小写,这样就可以对应TYPE_ALIASES中已注册的别名。

2、解析<typeAlias>标签

?上面分析了<package>标签的解析过程,下面看有关<typeAlias>标签的解析,

解析<typeAlias>标签即是获取alias和type两个属性,可以看到对alias进行了判断,也就说可以不配置alias属性,那么会使用下面的方法处理


下面看registerAlias(alias,type)方法,


public Configuration() {
    typeAliasRegistry.registerAlias("JDBC",JdbcTransactionFactory.);
    typeAliasRegistry.registerAlias("MANAGED",ManagedTransactionFactory.);

    typeAliasRegistry.registerAlias("JNDI",JndiDataSourceFactory.);
    typeAliasRegistry.registerAlias("POOLED",PooledDataSourceFactory.);
    typeAliasRegistry.registerAlias("UNPOOLED",UnpooledDataSourceFactory.);

    typeAliasRegistry.registerAlias("PERPETUAL",PerpetualCache.);
    typeAliasRegistry.registerAlias("FIFO",FifoCache.);
    typeAliasRegistry.registerAlias("LRU",LruCache.);
    typeAliasRegistry.registerAlias("SOFT",SoftCache.);
    typeAliasRegistry.registerAlias("WEAK",WeakCache.);

    typeAliasRegistry.registerAlias("DB_VENDOR",VendorDatabaseIdProvider.);

    typeAliasRegistry.registerAlias("XML",XMLLanguageDriver.);
    typeAliasRegistry.registerAlias("RAW",RawLanguageDriver.);

    typeAliasRegistry.registerAlias("SLF4J",Slf4jImpl.);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING",JakartaCommonsLoggingImpl.);
    typeAliasRegistry.registerAlias("LOG4J",Log4jImpl.);
    typeAliasRegistry.registerAlias("LOG4J2",Log4j2Impl.);
    typeAliasRegistry.registerAlias("JDK_LOGGING",Jdk14LoggingImpl.);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING",StdOutImpl.);
    typeAliasRegistry.registerAlias("NO_LOGGING",NoLoggingImpl.);

    typeAliasRegistry.registerAlias("CGLIB",CglibProxyFactory.);
    typeAliasRegistry.registerAlias("JAVASSIST",JavassistProxyFactory.);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.);
    languageRegistry.register(RawLanguageDriver.);
  }

在TypeAliasRegistry中注册了下面的别名,

默认的构造方法,初始化系统内置的别名
   TypeAliasRegistry() {
    registerAlias("string",String.);

    registerAlias("byte",Byte.);
    registerAlias("long",Long.);
    registerAlias("short",Short.);
    registerAlias("int",Integer.);
    registerAlias("integer",1)">);
    registerAlias("double",Double.);
    registerAlias("float",Float.);
    registerAlias("boolean",Boolean.);

    registerAlias("byte[]",Byte[].);
    registerAlias("long[]",Long[].);
    registerAlias("short[]",Short[].);
    registerAlias("int[]",Integer[].);
    registerAlias("integer[]",1)">);
    registerAlias("double[]",Double[].);
    registerAlias("float[]",Float[].);
    registerAlias("boolean[]",Boolean[].);

    registerAlias("_byte",byte.);
    registerAlias("_long",1)">long.);
    registerAlias("_short",1)">short.);
    registerAlias("_int",1)">int.);
    registerAlias("_integer",1)">);
    registerAlias("_double",1)">double.);
    registerAlias("_float",1)">float.);
    registerAlias("_boolean",1)">boolean.);

    registerAlias("_byte[]",1)">byte[].);
    registerAlias("_long[]",1)">long[].);
    registerAlias("_short[]",1)">short[].);
    registerAlias("_int[]",1)">int[].);
    registerAlias("_integer[]",1)">);
    registerAlias("_double[]",1)">double[].);
    registerAlias("_float[]",1)">float[].);
    registerAlias("_boolean[]",1)">boolean[].);

    registerAlias("date",Date.);
    registerAlias("decimal",BigDecimal.);
    registerAlias("bigdecimal",1)">);
    registerAlias("biginteger",BigInteger.);
    registerAlias("object",Object.);

    registerAlias("date[]",Date[].);
    registerAlias("decimal[]",BigDecimal[].);
    registerAlias("bigdecimal[]",1)">);
    registerAlias("biginteger[]",BigInteger[].);
    registerAlias("object[]",Object[].);

    registerAlias("map",Map.);
    registerAlias("hashmap",HashMap.);
    registerAlias("list",List.);
    registerAlias("arraylist",ArrayList.);
    registerAlias("collection",Collection.);
    registerAlias("iterator",Iterator.);

    registerAlias("ResultSet",ResultSet.);
  }

上面两个类注册了系统内置的别名,在核心配置文件和Mapper映射文件中可使用,mybatis会自动映射其注册类型,且大小写不区分。

?

原创不易,有不正之处欢迎指正。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读