MyBatis源码剖析

[MyBatis源码分析 - 类型模块 - 组件三] TypeA

2020-11-02  本文已影响0人  小胡_鸭

一、简介

  使用 MyBatis 时,最常规的用法是将 SQL 定义在 mapper xml 中,再用一个 mapper 接口文件去跟定义的 SQL 映射起来,在 mapper xml 中,跟类型相关的属性比如 typeparameterTyperesultType 需要指定类的全限定名,如 type="com.pojo.Role",如果包名比较长,再加上 mapper xml 中需要经常设定类的全限定名,使用起来就比较繁琐,所以在类型模块里面还有一个类型别名的子模块,用来解决上述的问题。

  假如开启了别名的功能,一般类的别名若没有显式指定则为简单类名的小写,如 com.pojo.Role 的别名默认为 role,使用时直接用别名代替全限定名即可,如 type="role"

二、配置

  开启别名的功能,需要在 mybatis-config.xml 中配置,配置的方式跟类型处理器类似,一样有两种,一种是单独指定一个 POJO 的别名,另一种是采用包扫描的方式,如下:

    <typeHandlers>
        <!-- 配置自定义的typeHandler -->
        <typeHandler jdbcType="VARCHAR" javaType="string" handler="ssm.chapter4.typeHandler.MyTypeHandler"/>
        
        <!-- 使用扫描方式配置typeHandler: 无法在<result>中通过指定javaType和jdbcType的方式来使用typeHandler -->        
        <package name="ssm.chapter4.typeHandler"/>
    </typeHandlers>

  使用类扫描的方式配置时,可以配置多个包扫描,但是不同包中类名有可能一样,导致别名注册冲突,@Alias 注解就是用来解决该问题,用来 POJO 类上,直接指定类的别名,避免冲突。

三、数据结构

  TypeAliasRegistry 只有一个 Map 成员,用来保存别名和类的映射关系。

private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

四、别名注册

registerAliases(String packageName)
registerAliases(String packageName, Class<?> superType)

【功能】包扫描批量注册别名。
【源码与注解】

  // 包扫描配置会调用本方法
  public void registerAliases(String packageName){
    registerAliases(packageName, Object.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);
      }
    }
  }

【解析】
  使用包扫描的方式配置,会调用这两个方法,先从包中获得指定的类型,默认为 Object,过滤掉接口、内部类和抽象类之后,调用 #registerAlias(Type) 注册。

registerAlias(Class<?> type)
registerAlias(String alias, Class<?> value)

【功能】注册类别名。
【源码与注解】

  public void registerAlias(Class<?> type) {
    String alias = type.getSimpleName();
    // 优先使用 @Alias 注解定义的别名
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    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");
    }
    // issue #748
    // 不区分大小写,别名统一处理成小写注册
    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() + "'.");
    }
    TYPE_ALIASES.put(key, value);
  }

【解析】
  优先使用 @Alias 定义的别名,否则使用简单类名,注册到注册表中时,别名统一处理成小写,并且不允许同一别名重复注册。

registerAlias(String alias, String value)

【功能】根据类的权限定名注册别名。
【源码与注解】

  public void registerAlias(String alias, String value) {
    try {
      // 根据权限定名,加载类对应的 Class 对象,再调用 #registerAlias(String, Class) 方法注册
      registerAlias(alias, Resources.classForName(value));
    } catch (ClassNotFoundException e) {
      throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
    }
  }

Map<String, Class<?>> getTypeAliases()

【功能】获取系统别名注册表信息。
【源码】

  public Map<String, Class<?>> getTypeAliases() {
    return Collections.unmodifiableMap(TYPE_ALIASES);
  }

【解析】
  这里有个值得学习的地方,不直接返回 TYPE_ALIASES,而是使用 Collections.unmodifiableMap 返回了一个不可被修改的备份,这样可以保证线程安全。

五、系统初始化

  MyBatis 初始化时,自动注册了一系列 Java 类型的别名,方便用户使用,源码如下:

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

  汇总成表格如下:

别名 Java 类型 是否支持数组
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal Bigdecimal
biginteger Biginteger
object Object
map Map
hashmap Hashmap
list List
arraylist Arraylist
collection Collection
iterator Iterator
ResultSet ResultSet

六、简单类型注册表

  SimpleTypeRegistry 简单注册表,用来判断类型是否为简单类型,源码如下:

public class SimpleTypeRegistry {

  private static final Set<Class<?>> SIMPLE_TYPE_SET = new HashSet<Class<?>>();

  static {
    SIMPLE_TYPE_SET.add(String.class);
    SIMPLE_TYPE_SET.add(Byte.class);
    SIMPLE_TYPE_SET.add(Short.class);
    SIMPLE_TYPE_SET.add(Character.class);
    SIMPLE_TYPE_SET.add(Integer.class);
    SIMPLE_TYPE_SET.add(Long.class);
    SIMPLE_TYPE_SET.add(Float.class);
    SIMPLE_TYPE_SET.add(Double.class);
    SIMPLE_TYPE_SET.add(Boolean.class);
    SIMPLE_TYPE_SET.add(Date.class);
    SIMPLE_TYPE_SET.add(Class.class);
    SIMPLE_TYPE_SET.add(BigInteger.class);
    SIMPLE_TYPE_SET.add(BigDecimal.class);
  }

  private SimpleTypeRegistry() {
    // Prevent Instantiation
  }

  public static boolean isSimpleType(Class<?> clazz) {
    return SIMPLE_TYPE_SET.contains(clazz);
  }

}
上一篇下一篇

猜你喜欢

热点阅读