mybatis加载typeAliases
上一篇文章讲完了mybatis启动的时候,加载settings,settings的加载是非常复杂的,通常对于应用来说,只需要默认配置就可以,如果需要根据本身的业务需要,对mybatis进一步优化的话,可以修改settings配置。
在加载完settings配置了之后,接着XMLConfigBuilder通过执行typeAliasesElement(root.evalNode(“typeAliases”))加载typeAliases配置,typeAliases是别名的意思,如果不配置typeAliases的话,在xml文件里面bean需要写上全路径名,例如com.gzwx.model.Leave。配置了别名之后,xml文件只需要写上相应的别名即可。
在mybatis的核心配置文件里面,别名的配置方式有2种。
<typeAliases>
<!--扫描com.gzwx.mybatis.model包下面所有的bean,并为当前包以及下面所有的后代包的每一个类都起一个默认别名,默认小写-->
<package name="com.gzwx.mybatis.model" />
</typeAliases>
<typeAliases>
<!--为单独com.gzwx.mybatis.model.Leave这个bean起别名,别名就是type的值-->
<typeAlias type="leave" alias="com.gzwx.mybatis.model.Leave"/>
</typeAliases>
为bean起别名了之后,那么在xml里面可以这样写
<!--leave就是typeAliase里面的别名-->
<select id="findLeaveById" resultType="leave">
select name,startDate,endDate,memo from oa_leave where id = #{id}
</select>
mybatis也可以通过直接注解的方式为单独的bean起别名,例如@Alias(“leave”)。接着看源码typeAliasesElement(root.evalNode(“typeAliases”)),从configuration里面获取到typeAliases节点,并通过typeAliasesElement解析该节点的内容
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//判断typeAliases有没有package属性,如果有,那么执行该方法
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
//如果没有,那么看有没有alias属性,找到所有的alias属性,并单独对一个一个的bean起别名
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
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);
}
}
}
}
}
先看package方式,configuration.getTypeAliasRegistry()是获取别名的注册器,这个方法就是在Configuration里面的
public TypeAliasRegistry getTypeAliasRegistry() {
return typeAliasRegistry;
}
而configuration的typeAliasRegistry这个变量是在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);
}
然后再看registerAliases方法,传入的是包名,它里面调用的是
public void registerAliases(String packageName, Class<?> superType){
//创建一个resolverUtil对象,这个对象专门用来根据包名,通过调用VFS对象里面的反射机制获取到这个包名下所有的类文件
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
//找到这个包名下所有的类文件,如果这个类是Object类型的话,那么把类假如到resolverUtil的matches集合里面
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
//返回matches集合的内容
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);
}
}
}
从上面的代码可以看到,在注册别名的时候,先是通过一系列反射,找到包名下所有的类文件,然后放到一个集合中,再遍历这个集合,对一个一个的类进行别名的注册。
public void registerAlias(Class<?> type) {
//获取类的名称
String alias = type.getSimpleName();
//获取这个类的Alias的注解,getAnnotition是jdk自带的方法,这个方法是根据传入的类获取到相关的注解
Alias aliasAnnotation = type.getAnnotation(Alias.class);
//如果这个注解存在,那么直接用注解里面的值,所以这里面就说明一个问题,注解的优先级是大于配置文件里面的
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
registerAlias(alias, type);
}
/**
*这个方法是把类放到TYPE_ALIASES集合里面,key就是alias,value就是传入的value
*
**/
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);
//如果原来集合里面已经包含了这个key,那么直接报错
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);
}
至此,对于package别名的注册已经完成了,接下来看对于单独一个一个alias配置的注册过程
//获取typeAliases节点下alias属性
String alias = child.getStringAttribute("alias");
//获取typeAliases节点下type属性
String type = child.getStringAttribute("type");
try {
//type的形式是:com.gzwx.mybatis.model.Leave,根据这个字符串反射出类
Class<?> clazz = Resources.classForName(type);
//如果在配置文件里面写了type那么直接把type作为key传进去注册,如果没写,那么在registerAlias里面通过getSimpleName()方法获取到类名,以类名进行注册,这个注册方法跟上面包名注册是一样的。
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种方法,第一种是注解的方法,这个优先级是最高的,第二种是通过核心配置里面的包名package批量设置,第三种是通过核心配置里面配置alias和type设置。mybatis在启动的时候,通过解析核心配置文件,反射出相应的类,作为value,如果设置了type就把type作为key,如果设置了注解,就把注解里面的value作为key,否则以类名作为key,保存到TYPE_ALIASES这个集合里面。TYPE_ALIASES是TypeAliasRegistry的一个变量,typeAliasRegistry是XMLConfigBuilder里面的一个变量,通过这样的方式完成了别名的设置。