MyBatis源码解析(八)——Type类型模块之TypeAliasRegistry(类型别名注册器)

原创作品,可以转载,但是要标注出处地址:http://www.cnblogs.com/V1haoge/p/6705769.html

1、回顾

  前面几首讲话了数据源模块,这与之前的事体模块都是environment中的部分,而Environgment是Configuration的根基部分,是构建Configuration的基础,但是生了基石只是会构建一个粗略的布对象,要服实际的采取环境,还需要分外的零件,这些事物还需加上到布置对象吃,这同赖就介绍类型模块—Type。

  类型模块的具体内容基本上还在org.apache.ibatis.type包下,类型模块也包括多情节,主要的哪怕是种的号注册器与种类处理器注册器,这片只注册器是故来统筹规划所有项目别名和档次处理器的,之后会以立刻片只注册器的实例配置到Configuration对象被;除此之外基本上是实际的种类处理器的兑现类似了,MyBatis内置了深完美的类型处理器实现,当然我们也可于定义实现项目处理器,经过简要的部署就得使的生效。

  这无异篇我们惟有研究项目别名注册器TypeAliasRegistry。

2、类型别名TypeAlias

  什么是种别名呢?

  MyBatis中之类型别名就是针对MyBatis中常用的门类进行别名设置,使用别名来代表具体的品种,简单点说哪怕是,将现实的花色因别名为键,保存至一个HashMap之中,方便存取。

  类型别名的用处是什么?

  MyBatis中的种类别名主要用以代替复杂的类别全限定名,用于映射器配置文件被进行参数类型及归结果类型的装置,MyBatis会在进展数据库操作前开展参数类型别名的辨析操作获取具体的参数类型,又会以数据库操作下展开结果类型别名的分析获取具体的结果类型,再经过后如果研究的类处理器进行项目处理来将参数与结果个别开展匹配映射。

2.1 基础项目别名

  那么MyBatis中坐的号到底有怎么样也?我们来探视源码:

 1 //构造函数里注册系统内置的类型别名  
 2 public TypeAliasRegistry() {
 3         //字符串类型
 4     registerAlias("string", String.class);
 5 
 6     //基本包装类型
 7     registerAlias("byte", Byte.class);
 8     registerAlias("long", Long.class);
 9     registerAlias("short", Short.class);
10     registerAlias("int", Integer.class);
11     registerAlias("integer", Integer.class);
12     registerAlias("double", Double.class);
13     registerAlias("float", Float.class);
14     registerAlias("boolean", Boolean.class);
15 
16     //基本数组包装类型
17     registerAlias("byte[]", Byte[].class);
18     registerAlias("long[]", Long[].class);
19     registerAlias("short[]", Short[].class);
20     registerAlias("int[]", Integer[].class);
21     registerAlias("integer[]", Integer[].class);
22     registerAlias("double[]", Double[].class);
23     registerAlias("float[]", Float[].class);
24     registerAlias("boolean[]", Boolean[].class);
25 
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     //加个下划线,就变成了基本数组类型
37     registerAlias("_byte[]", byte[].class);
38     registerAlias("_long[]", long[].class);
39     registerAlias("_short[]", short[].class);
40     registerAlias("_int[]", int[].class);
41     registerAlias("_integer[]", int[].class);
42     registerAlias("_double[]", double[].class);
43     registerAlias("_float[]", float[].class);
44     registerAlias("_boolean[]", boolean[].class);
45 
46     //日期数字型
47     registerAlias("date", Date.class);
48     registerAlias("decimal", BigDecimal.class);
49     registerAlias("bigdecimal", BigDecimal.class);
50     registerAlias("biginteger", BigInteger.class);
51     registerAlias("object", Object.class);
52 
53     registerAlias("date[]", Date[].class);
54     registerAlias("decimal[]", BigDecimal[].class);
55     registerAlias("bigdecimal[]", BigDecimal[].class);
56     registerAlias("biginteger[]", BigInteger[].class);
57     registerAlias("object[]", Object[].class);
58 
59     //集合型
60     registerAlias("map", Map.class);
61     registerAlias("hashmap", HashMap.class);
62     registerAlias("list", List.class);
63     registerAlias("arraylist", ArrayList.class);
64     registerAlias("collection", Collection.class);
65     registerAlias("iterator", Iterator.class);
66 
67     //还有个ResultSet型
68     registerAlias("ResultSet", ResultSet.class);
69  }     

  从点的源码中我们得以视,在档次别名注册器类TypeAliasRegistry的任参构造器中进行了大量之基础项目别名的注册(设置),涉及到的有:

    1.字符串类型(别名类似string)

    2.为主型包装器类型及其数组类型(别名类似byte、byte[])

    3.主导项目及其数组类型(别名类似_byte、_byte[])

    4.日子类型及其数组类型(别名类似date、date[])

    5.大数字型及其数组类型(别名类似bigdecimal、bigdecimal[])

    6.Object类型及其数组类型(别名类似object、object[])

    7.集合类型(别名类似collection、map、list、hsahmap、arraylist、iterator)

    8.ResultSet结实集类型(别叫也ResultSet)

  注意:这并无是一体底MyBatis内置的型别名,还有一部分类型别名是于开立Configuration实例的当儿在该不论是参构造器中展开挂号之,这里少不介绍。

2.2 内置方法

  MyBatis在TypeAliasRegistry类中定义了型别名集合HashMap的存取方法:

2.2.1 存方法——注册方式

  类中极基本之花色别名注册方式就是是:registerAlias(String alias,
Class<?> value)方法:

 1     //注册类型别名
 2   public void registerAlias(String alias, Class<?> value) {
 3     if (alias == null) {
 4       throw new TypeException("The parameter alias cannot be null");
 5     }
 6     // issue #748
 7     String key = alias.toLowerCase(Locale.ENGLISH);
 8     //如果已经存在key了,且value和之前不一致,报错
 9     //这里逻辑略显复杂,感觉没必要,一个key对一个value呗,存在key直接报错不就得了
10     if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
11       throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
12     }
13     TYPE_ALIASES.put(key, value);
14   }

  上面的中心注册方式非常简单,参数分别吗而设置的别名名称及要配合的类类型,其中TYPE_ALIASES就是注册器中定义之用来保存类型别名的HashMap集合:

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

  但当时单是骨干措施,其外还包在简单栽注册方式,一栽是包联登记方式、一种是依次登记之计。这片栽办法都是本着用户从定义别名注册而规划的。

2.2.1.1 包联登记方式

  该种方式对应之是之类的装置法:

1  <typeAliases>
2      <package name="com.xx.xx.xx"/>
3  </typeAliases>

  注册细节详见下方源码:

 1   public void registerAliases(String packageName){
 2     registerAliases(packageName, Object.class);
 3   }
 4 
 5     //扫描并注册包下所有继承于superType的类型别名
 6   public void registerAliases(String packageName, Class<?> superType){
 7         //TODO ResolverUtil
 8     ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
 9     resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
10     Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
11     for(Class<?> type : typeSet){
12       // Ignore inner classes and interfaces (including package-info.java)
13       // Skip also inner classes. See issue #6
14       if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
15         registerAlias(type);
16       }
17     }
18   }
19 
20     //注册类型别名
21   public void registerAlias(Class<?> type) {
22     //如果没有类型别名,用Class.getSimpleName来注册
23     String alias = type.getSimpleName();
24     //或者通过Alias注解来注册(Class.getAnnotation)
25     Alias aliasAnnotation = type.getAnnotation(Alias.class);
26     if (aliasAnnotation != null) {
27       alias = aliasAnnotation.value();
28     } 
29     registerAlias(alias, type);
30   }

  解析:

  上面的老三只章程逐个调用,最后重复调用核心注册方式进行最终的挂号工作,让咱来看望就三单艺术的目的所在。

  第一单办法吃单独来同句子代码,表示注册指定包名下的所有类,它调用了第二个章程,其第二个参数目的是限制要注册之好像的来源于,只有继承自给定类型的切近才能够让注册,这里赋值为Object.class表示该下的具有类似都以别名注册之考虑限。

  第二只章程被情节比较复杂,这个法的目的是为限制而登记号的类似的克,首先使参数superClass来界定只有继承自该类的好像才能够展开项目别名注册,然后又败内部类(包括匿名内部类、普通内部类)、接口类,通过调用第三只点子将多余的指定项目(type)进行路别名注册。

  注意:第二只措施被使了MyBatis中定义的一个工具类ResolverUtil来拓展落实,主要运用其进行点名包名下方类型的搜索find()与getClasses()方法

  第三独道要用来安装别名,其中有零星栽状况,第一栽是针对不显式指定别名名称的项目,通过Class.getSimpleName()方法来取得项目的别名名称,其拿走到之实际上是路的首字母小写形式的称呼,另一样栽状态是指向使应用@Alias注解显式指定别名名称的路(value的价),直接获得该注解中value的值当别名名称即可,最后调用核心注册方式来落实项目别名的挂号。

  上面的老三只办法各有打算,第一独对外获取包名,第二个限范围,第三单装别名,其中我们可以免指定别名也可以动用注解来显式指定别名,不指定别名最后别名会调用本地方法来抱。

2.2.1.2 逐个注册方式

  该方法对的凡之类的装置方式:

1  <typeAliases>
2      <typeAlias type="com.xxx.xx.Role" alias="role" />
3  </typeAliases>

  注册细节详见源码:

1   public void registerAlias(String alias, String value) {
2     try {
3       registerAlias(alias, Resources.classForName(value));
4     } catch (ClassNotFoundException e) {
5       throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e);
6     }
7   }

  解析:

  这种办法大粗略,主要是以于安中早就拿对象和所而设置的号名称还指定好了,因此只待一直实施核心注册方式即可到位登记工作,这种措施符合少量底路注册情况,但是要需要登记的型比较多,工作就会见显示十分是错综复杂繁琐,为了简化工作我们得采用前率先种植方式,要动用这种艺术将在架设编码时有意的用急需展开路别名注册的切近放置到统一之包下。

2.2.2 取方法—解析方法

  首先罗列方法源码:

 1   public <T> Class<T> resolveAlias(String string) {
 2     try {
 3       if (string == null) {
 4         return null;
 5       }
 6       // issue #748
 7       //先转成小写再解析
 8       //这里转个小写也有bug?见748号bug(在google code上)
 9       //https://code.google.com/p/mybatis/issues
10       //比如如果本地语言是Turkish,那i转成大写就不是I了,而是另外一个字符(İ)。这样土耳其的机器就用不了mybatis了!这是一个很大的bug,但是基本上每个人都会犯......
11       String key = string.toLowerCase(Locale.ENGLISH);
12       Class<T> value;
13       //原理就很简单了,从HashMap里找对应的键值,找到则返回类型别名对应的Class
14       if (TYPE_ALIASES.containsKey(key)) {
15         value = (Class<T>) TYPE_ALIASES.get(key);
16       } else {
17         //找不到,再试着将String直接转成Class(这样怪不得我们也可以直接用java.lang.Integer的方式定义,也可以就int这么定义)
18         value = (Class<T>) Resources.classForName(string);
19       }
20       return value;
21     } catch (ClassNotFoundException e) {
22       throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
23     }
24   }

  解析:

  结合注释内容,我们可以看出,所谓解析别名,就是通过别名获取集合中保留之号对应之价,即类项目。

  需要专注的哪怕是此要会在集中找到给定的号(别叫吧键),则直将相应之花色返回,但如果搜索不至号,这里会见尝试用别名直接转化成类型,能转化的别名必定是本着许类型的全限定名的字符串形式(例如:”java.lang.Integer”模式之号),这也便是我们当映射器文件被进行parameterType与ResultType设置的时光,可以形容成”int”的措施,也得描绘成”java.lang.Integer”的艺术了。因为此处解析别名的下会调用这个别名解析方法resolveAlias(),针对上述两种植情况还得以兑现科学解析。

3、模块分析

  所谓的模块分析是自我新增的一个内容,主要研究一下设置有模块的目的所在,这个模块于合MyBatis系统架构中之位置与用意,这片情节全是自家理解,若有错误与相差,还请求不吝赐教,我会马上更正。

  类型别名也是MyBatis整个架构中于主要的内容了,他一般是与类型处理器一起配合使用的。它的动是以映射器配置文件中在布置SQL脚本的签属性被。下面举个简单的以身作则:

1 <delete id="delete" parameterType="int">
2     DELETE TB_USER u WHERE u.use_id = #{useId}
3 </delete>
4 <select id="select" parameterType="int" resulttype="com.xxx.xx.User">
5     SELECT * FROM TB_USER u WHERE u.use_id=#{useId}
6 </select>

  上面示例中的parameterType与resultType的价就是项目别名,他们还是为字符串的形式出现,会以分析方法吃当键获取HashMap中的对应类类型值,或者直接转化为对应的色。

  这个小组件是映射器模块中的一个重点组件,因为咱们采取XML形式展开映射器配置,那么不可避免的在XML文件中无可能出现确实的Java类型,多是字符串,这样咱们尽管待以MyBatis中多一个解析XML映射配置中吃定类型字符串的计来得到其所代表的Java类型。(这里的字符串正是类型别名)

  XML映射器就是对Java类型与数据库类型中进行映射转换,以这来兑现ORM功能。

  那么,这样一来,我们不怕掌握了型别名注册器在满MyBatis中之职务及作用。

(作者:唯一浩哥)

网站地图xml地图