Mybatis别名注册器逻辑分析

一、前言

在Mybatis编程中我们经常会用到将某个bean作为参数类型parameterType或者结果返回值类型ResultType,所以很多时候我们需要把完成的Bean的包名在mapper文件中写上,如下:

<select id="selectUser" parameterType="com.dy.entity.User" resultType="com.dy.entity.User">  
    select * from user where c_id=#{id}  
</select>  

Mybatis给我们提供了一种叫别名的机制,意思就是对某个具体的类设置别名,在mybatis的配置文件中配置如下:

<configuration>
    <typeAliases>
      <!--
      通过package, 可以直接指定package的名字, mybatis会自动扫描你指定包下面的javabean,
      并且默认设置一个别名,默认的名字为: javabean 的首字母小写的非限定类名来作为它的别名。
      也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user) 
      <package name="com.dy.entity"/>
       -->
      <typeAlias alias="user" type="com.dy.entity.User"/>
  </typeAliases>
  
  ......
  
</configuration>

这样之后mapper文件中的select可以写成如下格式:

<select id="selectUser" parameterType="user" resultType="user">  
    select * from user where c_id=#{id}  
</select>  

这样就可以在使用某个bean时使用别名就可以了,不需要写完成的包名+类名。目前Mybatis提供2中别名注册方式typeAlias和package

Mybatis别名注册器是TypeAliasRegistry,其最底层注册逻辑是下面的registerAlias方法

public class TypeAliasRegistry {
  
  //别名池
  private final Map<String, Class<?>> typeAliases = new HashMap<>();
  
  //忽略代码
 
 public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // PERdd 会变成perdd
    String key = alias.toLowerCase(Locale.ENGLISH);
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("");
    }
    typeAliases.put(key, value);
  }
}

二、注册逻辑分析

XMLConfigBuilder的parseConfiguration方法中的typeAliasesElement(root.evalNode("typeAliases"));代码片段开始

public class XMLConfigBuilder extends BaseBuilder {

  private void typeAliasesElement(XNode parent) {
     if (parent != null) {
       for (XNode child : parent.getChildren()) {
        //从这可以看出package类型级别大于typeAlias
        if ("package".equals(child.getName())) {
          String typeAliasPackage = child.getStringAttribute("name");
          //利用typeAliasRegistry把他注册到别名池中
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } 
        //如果是typeAlias类型
        else {
          //获取别名alias,和类型type
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type)if (alias == null) {
            //如果没有显示指定alias值,那么进入这个注册逻辑
              typeAliasRegistry.registerAlias(clazz);
            } else {
             //如果显示指定alias值,那么进入这个注册逻辑
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

package别名注册器和typeAlias别名注册器只能存在一个,且package类型注册别名优先级大于typeAlias类型别名注册,他们的处理过程下面分析

2.1 package类型别名注册逻辑
<typeAliases>
      <package name="com.clyu.bean"/>
</typeAliases>

1、在使用package类型注册别名中,优先注册@Alias注解显示指定的别名;
2、如果没有标注@Alias注解,那么其别名时类名字母小写(如:PERdd他的别名就是perdd)

public class TypeAliasRegistry {
  //别名池
  private final Map<String, Class<?>> typeAliases = new HashMap<>();
  
  public void registerAliases(String packageName) {
    registerAliases(packageName, Object.class);
  }
  
  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) {
      // 如果当前对象不是是匿名类,不是接口类型,不是Member类型
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    
  public void registerAlias(Class<?> type) {
    //获取一个类的简单名字,如com.clyu.Person返回的就是Person
    String alias = type.getSimpleName();
    //获取当类上的@Alias注解信息
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    //如果当前类上有@Alias,那么别名就是@Alias的值
    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");
    }
    // 别名字母小写化
    String key = alias.toLowerCase(Locale.ENGLISH);
    //判断别名池中是否存在,如果出现2个类名一样,但包不一样的,就会报错
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException(".......");
    }
    //注册到别名池名,别名为key,类名为value
    typeAliases.put(key, value);
  }

}

注意:如果package注册器指定的包下如果出现2个类名一样,但包不一样的,就会报错。

2.2 typeAlias类型别名注册逻辑

typeAlias类型别名注册有2种逻辑,一种是没有显示指定alias值,另一种是显示指定alias值,

没有显示指定alias值

 <typeAliases>
    <typeAlias type="com.clyu.bean.Person"></typeAlias>
  </typeAliases>
public class TypeAliasRegistry {
  //别名池
  private final Map<String, Class<?>> typeAliases = new HashMap<>();
  
  public void registerAlias(Class<?> type) {
    //获取一个类的简单名字,如com.clyu.Person返回的就是Person
    String alias = type.getSimpleName();
    //获取当类上的@Alias注解信息
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    //如果当前类上有@Alias,那么别名就是@Alias的值
    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");
    }
    // 别名小写,如:PERSon 就是person
    String key = alias.toLowerCase(Locale.ENGLISH);
    //判断别名池中是否存在
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException(".......");
    }
    //注册到别名池名,别名为key,类名为value
    typeAliases.put(key, value);
  }
}

处理逻辑基本上和package类型别名注册逻辑一样

显示指定alias值

 <typeAliases>
    <!--<package name="com.clyu.bean"/>-->
    <typeAlias type="com.clyu.bean.Person" alias="perosn"></typeAlias>
  </typeAliases>
public class TypeAliasRegistry {
  //别名池
  private final Map<String, Class<?>> typeAliases = new HashMap<>();
 
  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // 别名首字母小写化
    String key = alias.toLowerCase(Locale.ENGLISH);
    //判断别名池中是否存在
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException(".......");
    }
    //注册到别名池名,别名为key,类名为value
    typeAliases.put(key, value);
  }
}

结论:在使用typeAlias类型注册别名中,如果没有显示指定alias值,那么别名默认是类名的小写,我们可以使用@Alias注解显示指定别名;如果显示指定alias值,那么@Alias将不生效;

三、默认别名

TypeAliasRegistry是Mybatis别名注册池,其在SqlSessionFactory的实例化中进行了2次默认别名注册,第一次是在TypeAliasRegistry的构造器中,第二次是在Configuration的构造器中

第一次注册默认别名

public class TypeAliasRegistry {
  //key是别名,值是对应的java类型
  private final Map<String, Class<?>> typeAliases = new HashMap<>();

  public TypeAliasRegistry() {
    registerAlias("string", String.class);

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

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

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

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

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

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

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

    registerAlias("ResultSet", ResultSet.class);
  }
}

第二次注册默认别名

public class Configuration {

   //忽略代码
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  //忽略代码

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

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

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

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

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

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

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

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }
    //忽略代码
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值