一、前言
在Mybatis编程中我们经常会用到将某个bean作为参数类型parameterType或者结果返回值类型ResultType,所以很多时候我们需要把完成的Bean的包名在mapper文件中写上,如下:
<select id="selectUser" parameterType="com.dy.entity.User" resultType="com.dy.entity.User">
select * from user where c_id=#{id}
</select>
Mybatis给我们提供了一种叫别名的机制,意思就是对某个具体的类设置别名,在mybatis的配置文件中配置如下:
<configuration>
<typeAliases>
<!--
通过package, 可以直接指定package的名字, mybatis会自动扫描你指定包下面的javabean,
并且默认设置一个别名,默认的名字为: javabean 的首字母小写的非限定类名来作为它的别名。
也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user)
<package name="com.dy.entity"/>
-->
<typeAlias alias="user" type="com.dy.entity.User"/>
</typeAliases>
......
</configuration>
这样之后mapper文件中的select可以写成如下格式:
<select id="selectUser" parameterType="user" resultType="user">
select * from user where c_id=#{id}
</select>
这样就可以在使用某个bean时使用别名就可以了,不需要写完成的包名+类名。目前Mybatis提供2中别名注册方式typeAlias和package
Mybatis别名注册器是TypeAliasRegistry,其最底层注册逻辑是下面的registerAlias方法
public class TypeAliasRegistry {
//别名池
private final Map<String, Class<?>> typeAliases = new HashMap<>();
//忽略代码
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// PERdd 会变成perdd
String key = alias.toLowerCase(Locale.ENGLISH);
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("");
}
typeAliases.put(key, value);
}
}
二、注册逻辑分析
从XMLConfigBuilder
的parseConfiguration方法中的typeAliasesElement(root.evalNode("typeAliases"));
代码片段开始
public class XMLConfigBuilder extends BaseBuilder {
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//从这可以看出package类型级别大于typeAlias
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
//利用typeAliasRegistry把他注册到别名池中
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
}
//如果是typeAlias类型
else {
//获取别名alias,和类型type
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
//如果没有显示指定alias值,那么进入这个注册逻辑
typeAliasRegistry.registerAlias(clazz);
} else {
//如果显示指定alias值,那么进入这个注册逻辑
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
package别名注册器和typeAlias别名注册器只能存在一个,且package类型注册别名优先级大于typeAlias类型别名注册,他们的处理过程下面分析
2.1 package类型别名注册逻辑
<typeAliases>
<package name="com.clyu.bean"/>
</typeAliases>
1、在使用package类型注册别名中,优先注册@Alias注解显示指定的别名;
2、如果没有标注@Alias注解,那么其别名时类名字母小写(如:PERdd他的别名就是perdd)
public class TypeAliasRegistry {
//别名池
private final Map<String, Class<?>> typeAliases = new HashMap<>();
public void registerAliases(String packageName) {
registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
for (Class<?> type : typeSet) {
// 如果当前对象不是是匿名类,不是接口类型,不是Member类型
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
public void registerAlias(Class<?> type) {
//获取一个类的简单名字,如com.clyu.Person返回的就是Person
String alias = type.getSimpleName();
//获取当类上的@Alias注解信息
Alias aliasAnnotation = type.getAnnotation(Alias.class);
//如果当前类上有@Alias,那么别名就是@Alias的值
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");
}
// 别名字母小写化
String key = alias.toLowerCase(Locale.ENGLISH);
//判断别名池中是否存在,如果出现2个类名一样,但包不一样的,就会报错
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException(".......");
}
//注册到别名池名,别名为key,类名为value
typeAliases.put(key, value);
}
}
注意:如果package注册器指定的包下如果出现2个类名一样,但包不一样的,就会报错。
2.2 typeAlias类型别名注册逻辑
typeAlias类型别名注册有2种逻辑,一种是没有显示指定alias值,另一种是显示指定alias值,
没有显示指定alias值
<typeAliases>
<typeAlias type="com.clyu.bean.Person"></typeAlias>
</typeAliases>
public class TypeAliasRegistry {
//别名池
private final Map<String, Class<?>> typeAliases = new HashMap<>();
public void registerAlias(Class<?> type) {
//获取一个类的简单名字,如com.clyu.Person返回的就是Person
String alias = type.getSimpleName();
//获取当类上的@Alias注解信息
Alias aliasAnnotation = type.getAnnotation(Alias.class);
//如果当前类上有@Alias,那么别名就是@Alias的值
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");
}
// 别名小写,如:PERSon 就是person
String key = alias.toLowerCase(Locale.ENGLISH);
//判断别名池中是否存在
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException(".......");
}
//注册到别名池名,别名为key,类名为value
typeAliases.put(key, value);
}
}
处理逻辑基本上和package类型别名注册逻辑一样
显示指定alias值
<typeAliases>
<!--<package name="com.clyu.bean"/>-->
<typeAlias type="com.clyu.bean.Person" alias="perosn"></typeAlias>
</typeAliases>
public class TypeAliasRegistry {
//别名池
private final Map<String, Class<?>> typeAliases = new HashMap<>();
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// 别名首字母小写化
String key = alias.toLowerCase(Locale.ENGLISH);
//判断别名池中是否存在
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException(".......");
}
//注册到别名池名,别名为key,类名为value
typeAliases.put(key, value);
}
}
结论:在使用typeAlias类型注册别名中,如果没有显示指定alias值,那么别名默认是类名的小写,我们可以使用@Alias注解显示指定别名;如果显示指定alias值,那么@Alias将不生效;
三、默认别名
TypeAliasRegistry是Mybatis别名注册池,其在SqlSessionFactory的实例化中进行了2次默认别名注册,第一次是在TypeAliasRegistry的构造器中,第二次是在Configuration的构造器中
第一次注册默认别名
public class TypeAliasRegistry {
//key是别名,值是对应的java类型
private final Map<String, Class<?>> typeAliases = new HashMap<>();
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);
}
}
第二次注册默认别名
public class Configuration {
//忽略代码
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
//忽略代码
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
//忽略代码
}