Mybatis源码之(TypeAliasRegistry)TypeAlias别名实现机制

出自:https://blog.csdn.net/qq924862077/article/details/52612589

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

[html]  view plain  copy
  1. <select id="selectUser" parameterType="com.dy.entity.User" resultType="com.dy.entity.User">    
  2.     select * from user where c_id=#{id}    
  3. </select>    

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

[html]  view plain  copy
  1. <configuration>  
  2.     <typeAliases>  
  3.       <!--  
  4.       通过package, 可以直接指定package的名字, mybatis会自动扫描你指定包下面的javabean,  
  5.       并且默认设置一个别名,默认的名字为: javabean 的首字母小写的非限定类名来作为它的别名。  
  6.       也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user)   
  7.       <package name="com.dy.entity"/>  
  8.        -->  
  9.       <typeAlias alias="user" type="com.dy.entity.User"/>  
  10.   </typeAliases>  
  11.     
  12.   ......  
  13.     
  14. </configuration>  

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

[html]  view plain  copy
  1. <select id="selectUser" parameterType="user" resultType="user">    
  2.     select * from user where c_id=#{id}    
  3. </select>    

这样就可以在使用某个bean时使用别名就可以了,不需要写完成的包名+类名

接下来我们介绍TypeAlias别名的实现机制

(1)我们在mybatis的配置文件中配置了typeAliases,我们首先分析XMLConfigBuilder类中对于typeAliases的解析,源码如下:

[java]  view plain  copy
  1. //类别名解析  
  2.   private void typeAliasesElement(XNode parent) {  
  3.     if (parent != null) {  
  4.       for (XNode child : parent.getChildren()) {  
  5.         //如果子节点是package,那么就获取package节点的name属性  
  6.         if ("package".equals(child.getName())) {  
  7.           String typeAliasPackage = child.getStringAttribute("name");  
  8.           configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);  
  9.         } else {  
  10.         //如果子节点是typeAlias节点,那么就获取alias属性和type的属性  
  11.           String alias = child.getStringAttribute("alias");  
  12.           String type = child.getStringAttribute("type");  
  13.           try {  
  14.             //通过type的值来加载获得类  
  15.             Class<?> clazz = Resources.classForName(type);  
  16.             if (alias == null) {  
  17.             //typeAliasRegistry会进行别名注册  
  18.               typeAliasRegistry.registerAlias(clazz);  
  19.             } else {  
  20.               typeAliasRegistry.registerAlias(alias, clazz);  
  21.             }  
  22.           } catch (ClassNotFoundException e) {  
  23.             throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);  
  24.           }  
  25.         }  
  26.       }  
  27.     }  

通过分析源码我们可以得知,解析alias来获得别名,解析type元素来获得类名,通过Resources.classForName(type)获得类信息,然后通过typeAliasRegistry.registerAlias(alias, clazz)将类别名注册到typeAliasRegistry中,这样就完成了mybatis中配置文件的解析。

(3)TypeAliasRegistry:是用来记录别名alias和类clazz之间的对应关系的,它可以看做是一个Map,alias作为key,类名作为value,详看源码如下:

[java]  view plain  copy
  1. //其实就是一个map结构,用来对象key别名和value具体的类  
  2. public class TypeAliasRegistry {  
  3.   
  4.   private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();  
  5.   
  6.   public TypeAliasRegistry() {  
  7.     registerAlias("string", String.class);  
  8.   
  9.     registerAlias("byte", Byte.class);  
  10.     registerAlias("long", Long.class);  
  11.     registerAlias("short", Short.class);  
  12.     registerAlias("int", Integer.class);  
  13.     registerAlias("integer", Integer.class);  
  14.     registerAlias("double", Double.class);  
  15.     registerAlias("float", Float.class);  
  16.     registerAlias("boolean", Boolean.class);  
  17.   
  18.     registerAlias("byte[]", Byte[].class);  
  19.     registerAlias("long[]", Long[].class);  
  20.     registerAlias("short[]", Short[].class);  
  21.     registerAlias("int[]", Integer[].class);  
  22.     registerAlias("integer[]", Integer[].class);  
  23.     registerAlias("double[]", Double[].class);  
  24.     registerAlias("float[]", Float[].class);  
  25.     registerAlias("boolean[]", Boolean[].class);  
  26.   
  27.     registerAlias("_byte"byte.class);  
  28.     registerAlias("_long"long.class);  
  29.     registerAlias("_short"short.class);  
  30.     registerAlias("_int"int.class);  
  31.     registerAlias("_integer"int.class);  
  32.     registerAlias("_double"double.class);  
  33.     registerAlias("_float"float.class);  
  34.     registerAlias("_boolean"boolean.class);  
  35.   
  36.     registerAlias("_byte[]"byte[].class);  
  37.     registerAlias("_long[]"long[].class);  
  38.     registerAlias("_short[]"short[].class);  
  39.     registerAlias("_int[]"int[].class);  
  40.     registerAlias("_integer[]"int[].class);  
  41.     registerAlias("_double[]"double[].class);  
  42.     registerAlias("_float[]"float[].class);  
  43.     registerAlias("_boolean[]"boolean[].class);  
  44.   
  45.     registerAlias("date", Date.class);  
  46.     registerAlias("decimal", BigDecimal.class);  
  47.     registerAlias("bigdecimal", BigDecimal.class);  
  48.     registerAlias("biginteger", BigInteger.class);  
  49.     registerAlias("object", Object.class);  
  50.   
  51.     registerAlias("date[]", Date[].class);  
  52.     registerAlias("decimal[]", BigDecimal[].class);  
  53.     registerAlias("bigdecimal[]", BigDecimal[].class);  
  54.     registerAlias("biginteger[]", BigInteger[].class);  
  55.     registerAlias("object[]", Object[].class);  
  56.   
  57.     registerAlias("map", Map.class);  
  58.     registerAlias("hashmap", HashMap.class);  
  59.     registerAlias("list", List.class);  
  60.     registerAlias("arraylist", ArrayList.class);  
  61.     registerAlias("collection", Collection.class);  
  62.     registerAlias("iterator", Iterator.class);  
  63.   
  64.     registerAlias("ResultSet", ResultSet.class);  
  65.   }  
  66.   
  67.   @SuppressWarnings("unchecked")  
  68.   // throws class cast exception as well if types cannot be assigned  
  69.   /* 通过别名来找到具体的类**/  
  70.   public <T> Class<T> resolveAlias(String string) {  
  71.     try {  
  72.       if (string == null) {  
  73.         return null;  
  74.       }  
  75.       // issue #748  
  76.       String key = string.toLowerCase(Locale.ENGLISH);  
  77.       Class<T> value;  
  78.       if (TYPE_ALIASES.containsKey(key)) {  
  79.         value = (Class<T>) TYPE_ALIASES.get(key);  
  80.       } else {  
  81.         value = (Class<T>) Resources.classForName(string);  
  82.       }  
  83.       return value;  
  84.     } catch (ClassNotFoundException e) {  
  85.       throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);  
  86.     }  
  87.   }  
  88.   /* 通过包名注册类**/  
  89.   public void registerAliases(String packageName){  
  90.     registerAliases(packageName, Object.class);  
  91.   }  
  92.   /* 获得包内的类,除去内部类和接口**/  
  93.   public void registerAliases(String packageName, Class<?> superType){  
  94.     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();  
  95.     resolverUtil.find(new ResolverUtil.IsA(superType), packageName);  
  96.     Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();  
  97.     for(Class<?> type : typeSet){  
  98.       // Ignore inner classes and interfaces (including package-info.java)  
  99.       // Skip also inner classes. See issue #6  
  100.       if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {  
  101.         registerAlias(type);  
  102.       }  
  103.     }  
  104.   }  
  105.   /* 注册类**/  
  106.   public void registerAlias(Class<?> type) {  
  107.     String alias = type.getSimpleName();  
  108.     Alias aliasAnnotation = type.getAnnotation(Alias.class);  
  109.     if (aliasAnnotation != null) {  
  110.       alias = aliasAnnotation.value();  
  111.     }   
  112.     registerAlias(alias, type);  
  113.   }  
  114.   /* 注册类包括别名和类**/  
  115.   public void registerAlias(String alias, Class<?> value) {  
  116.     if (alias == null) {  
  117.       throw new TypeException("The parameter alias cannot be null");  
  118.     }  
  119.     // issue #748  
  120.     String key = alias.toLowerCase(Locale.ENGLISH);  
  121.     if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {  
  122.       throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");  
  123.     }  
  124.     TYPE_ALIASES.put(key, value);  
  125.   }  
  126.   /* 注册类包括别名和类名**/  
  127.   public void registerAlias(String alias, String value) {  
  128.     try {  
  129.       registerAlias(alias, Resources.classForName(value));  
  130.     } catch (ClassNotFoundException e) {  
  131.       throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);  
  132.     }  
  133.   }  
  134.     
  135.   /** 
  136.    * @since 3.2.2 
  137.    */  
  138.   public Map<String, Class<?>> getTypeAliases() {  
  139.     return Collections.unmodifiableMap(TYPE_ALIASES);  
  140.   }  
  141.   
  142. }  

通过上面的源码我们可以看到,它默认注册了一些基本的类型基本类和包装类,然后我们可以调用registerAliases来注册其他类的别名。

(3)刚才我们看到了TypeAliasRegistry.registerAliases()函数会登记别名及类名,我们也可以看到TypeAliasRegistry通过了resolveAlias函数来让我们通过别名alias来获取实际的类,源码如下:

[java]  view plain  copy
  1. /* 通过别名来找到具体的类**/  
  2.   public <T> Class<T> resolveAlias(String string) {  
  3.     try {  
  4.       if (string == null) {  
  5.         return null;  
  6.       }  
  7.       // issue #748  
  8.       String key = string.toLowerCase(Locale.ENGLISH);  
  9.       Class<T> value;  
  10.       if (TYPE_ALIASES.containsKey(key)) {  
  11.         value = (Class<T>) TYPE_ALIASES.get(key);  
  12.       } else {  
  13.         value = (Class<T>) Resources.classForName(string);  
  14.       }  
  15.       return value;  
  16.     } catch (ClassNotFoundException e) {  
  17.       throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);  
  18.     }  
  19.   }  

总结:这样我们就对Mybatis的typaAlias的实现机制就有了一个简单的了解,其实简单说就是创建了一个Map<string,Class<?>>,解析mybatis的配置文件,将alias元素的值作为Map的key,通过反射机制获得的type元素对应的类名的类作为Map的value值,在真正使用时通过alias别名来获取真正的类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值