美文网首页MyBatis源码剖析
[MyBatis源码分析 - 类型模块 - 组件三] TypeA

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

作者: 小胡_鸭 | 来源:发表于2020-11-02 00:03 被阅读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);
      }
    
    }
    

    相关文章

      网友评论

        本文标题:[MyBatis源码分析 - 类型模块 - 组件三] TypeA

        本文链接:https://www.haomeiwen.com/subject/uzyyvktx.html