mybatis的别名注册表 TypeAliasRegistry ,也是 mybatis 管理的一个别名容器,同样也是存放在Configuration中的。别名的好处就是: 当使用时不用再去写类的全限定名(包名.类名),使用简单的类名(全小写)即可 。使用多见与以下场景:
- ResultMap中的type,resultType,ofType,javaType, Type Handler等属性。
1.别名的注册
1.1.别名注册详解
TypeAliasRegistry为Configuration的成员变量,当初始化Configuration对象时,会执行如下动作:
- 首先会创建TypeAliasRegistry对象,将type相关的alia注册到TypeAliasRegistry中。
- 然后将Configuration相关的typeAlias注册到TypeAliasRegistry。
public class TypeAliasRegistry {
private final Map<String, Class<?>> typeAliases = new HashMap<>();
public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias(" Byte ", Byte.class);
registerAlias("long", Long.class);
// 省略......
}
}
public class Configuration {
// 1.创建TypeAliasRegistry对象
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
public Configuration() {
// 2.将相关typeAlias注册进来
typeAliasRegistry.registerAlias(" Jdbc ", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
// 省略......
}
}
最终类的别名都会存到TypeAliasRegistry的typeAliases变量中,该变量为HashMap结构.key为别名(全小写).value为key对应类的Class对象,用于框架反射创建实例。 结构见下图
别名注册方法详解( 码-1 )
public class TypeAliasRegistry{
private final Map<String, Class<?>> type Aliases = new HashMap<>();
// 只有Class,没有别名.这里会推测alias
public void registerAlias(Class<?> type) {
// 类名(除包名)
String alias = type.getSimpleName();
Alias aliasAnnotation = type.getAnnotation(Alias.class);
// 如果类上加了@Alias注解,那么alias = @Alias的value属性
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}
// 这方法会直接将别名放入typeAliases容器中
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 (typeAliases.containsKey(key) &&
typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '"
+ typeAliases.get(key).getName() + "'.");
}
// 存储结果,见上图
typeAliases.put(key, value);
}
}
1.2.系统默认别名列表
左侧为Configuration初始化时生成,右侧为TypeAliasRegistry初始化时生成。
2.自定义别名注册
别名注册分为两种形式,分别为:扫描包注册;单个类注册
2.1.扫描包
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// 省略......
}
}
}
}
public class ResolverUtil<T> {
// 匹配到的Class
private Set<Class<? extends T>> matches = new Hash Set<>();
public void registerAliases(String packageName) {
registerAliases( package Name, Object.class);
}
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
// 这里判断该包下是否都是Object的子类(上面的方法传过来的是Object.class),
// 如果是将该包下的所有类放到成员变量matches中
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
// 遍历matches
for (Class<?> type : typeSet) {
// Ignore inner classes and interfaces (including package-info.java)
// Skip also inner classes. See issue #6
// 当type不是匿名类、不是接口、不是成员类时,注册该type
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
// 注册参照【码-1】
registerAlias(type);
}
}
}
}
2.2.单个类
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 省略......
} else {
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
// 根据我们写的类的全限定名,反射获取Class
Class<?> clazz = Resources.classForName(type);
// 注册参照【码-1】
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias
+ "'. Cause: " + e, e);
}
}
}
}
}
3.别名的使用
通过别名找Class的方法为: 通过别名寻找.如果没有找到,通过将参数作为类的全限定名获取Class.这就是为什么当我们的resultType设置为int或integer时,mybatis也会自动帮我们映射成 Integer (参照1.2.系统默认别名列表).至于从怎么从Integer寻找相应的类型处理器,我们第二章接着分享.
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {
return null;
}
// issue #748
String key = string.toLowerCase(Locale.ENGLISH);
Class<T> value;
// 看这里,如果有别名的map中有这个key,那么就直接取出相应的Class即可
if (typeAliases.containsKey(key)) {
value = (Class<T>) typeAliases.get(key);
// 如果没有,根据Class.forName(),获取Class,如果这里不是写的类的全限定名,会保错ClassNotFound
} else {
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
4.结论
注册时:
- 别名注册时,首先判断类上是否有 @Alias 注解,如果有则获取 @Alias的value 属性,作为别名的初始值;如果没有则获取 类名 (不含包名)作为别名的初始值( 具体方法Class.getSimpleName() )。
- 将别名初始值的 小写 作为key,类的Class作为value放入TypeAliasRegistry的容器中. 注意:当有两个重名的别名时(包不同,但类名相同),会报错。
使用时:
- 将别名全部转换为小写,从TypeAliasRegistry容器中获取,所以 当我们使用别名时,注意是小写,不是驼峰。
- 如果没有获取到,会根据传入的别名进行Class.forName(),如果存在,在获取Class成功,否则会报ClassNotFoundException。