[MyBatis源码分析 - 类型模块 - 组件三] TypeA
一、简介
使用 MyBatis 时,最常规的用法是将 SQL 定义在 mapper xml 中,再用一个 mapper 接口文件去跟定义的 SQL 映射起来,在 mapper xml 中,跟类型相关的属性比如 type
、parameterType
、resultType
需要指定类的全限定名,如 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);
}
}