添加配置settings和typeAliases:
<configuration>
<properties resource="db.properties">
<property name="userName" value="root"/>
<property name="password" value="123456"/>
</properties>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
</settings>
<typeAliases>
<typeAlias type="cn.zsm.mybatis.student.Student" alias="student"/>
<package name="cn.zsm.mybatis.man"/>
</typeAliases>
<environments default="development">
.......
注意,标签的顺序不能乱,否则会报:Error creating document instance. Cause: org.xml.sax.SAXParseException;
启动测试类,查看标签解析过程:
解析settings标签和typeAliases标签的方法:
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings); //解析并设置vfs实现
this.loadCustomLogImpl(settings); //解析并设置日志实现
this.typeAliasesElement(root.evalNode("typeAliases"));
......
this.settingsElement(settings); //设置其他属性
settingsAsProperties:
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
} else {
//获取配置文件中setting配置
Properties props = context.getChildrenAsProperties();
//获取所有可以设置的属性信息
MetaClass metaConfig = MetaClass.forClass(Configuration.class, this.localReflectorFactory);
//迭代配置文件中的属性信息
Iterator var4 = props.keySet().iterator();
Object key;
do {
if (!var4.hasNext()) {
return props;
}
key = var4.next();
} while(metaConfig.hasSetter(String.valueOf(key))); //如果配置信息是可配置的属性,则继续,否则抛异常
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
解析settings标签返回一个Properties类,这个类是HashTable的子类,也就是说Properties其实就是个Map集合:
public class Properties extends Hashtable<Object,Object>{......}
在settingsAsProperties方法中,另一个比较重要的类是MetaClass,用来存储mybatis内置的设置的属性信息,只有是这些属性时,才会解析存储,否则会抛出异常BuilderException。
MetaClass:
MetaClass中有两个比较重要的属性reflectorFactory和reflector
public class MetaClass {
private final ReflectorFactory reflectorFactory;
private final Reflector reflector;
.......
}
ReflectorFactory:
public interface ReflectorFactory {
boolean isClassCacheEnabled();
void setClassCacheEnabled(boolean var1);
Reflector findForClass(Class<?> var1);
}
ReflectorFactory,根据类名就可以判断出它是Reflrector工厂,又来生成Reflector实例。
Reflector:
public class Reflector {
private final Class<?> type;
private final String[] readablePropertyNames;
private final String[] writeablePropertyNames;
private final Map<String, Invoker> setMethods = new HashMap();
private final Map<String, Invoker> getMethods = new HashMap();
private final Map<String, Class<?>> setTypes = new HashMap();
private final Map<String, Class<?>> getTypes = new HashMap();
private Constructor<?> defaultConstructor;
private Map<String, String> caseInsensitivePropertyMap = new HashMap();
......
}
Reflector中定义了很多的Map对象,用于存放各种方法和类信息,我们debug模式查看该类中都存放了哪些信息:
props中存放我们配置文件中settings标签下的信息:
metaConfig:
setMethods、getMethods、setTypes、getTypes这四个属性中存放了设置settings中可以设置的所有属性信息:
看看这里的信息和我们在settings中可以设置的信息是不是一样,如果不了解settings中可以设置的信息可以参考博文:Mybatis实践篇(四)中:Setting:mybatis行为设置。
typeAliasesElement:
private void typeAliasesElement(XNode parent) {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String alias;
//如果子节点是package, 那么就获取package节点的name属性, mybatis会扫描指定的package
if ("package".equals(child.getName())) {
alias = child.getStringAttribute("name");
//TypeAliasRegistry 负责管理别名,通过TypeAliasRegistry 进行别名注册
this.configuration.getTypeAliasRegistry().registerAliases(alias);
} else {
//如果子节点是typeAlias节点,那么就获取alias属性和type的属性值
alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
this.typeAliasRegistry.registerAlias(clazz);
} else {
this.typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException var7) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + var7, var7);
}
}
}
}
}
typeAliasesElement的解析主要就是注册bean以及beanName,而通过上面的源码可以看出,mybatis注册bean是通过registerAliases方法实现的,线面我们就来看看registerAliases方法的源码。
TypeAliasRegistry :
public class TypeAliasRegistry {
private final Map<String, Class<?>> TYPE_ALIASES = new HashMap();
//mybatis默认设置的别名,这里省略了很多
public TypeAliasRegistry() {
this.registerAlias("string", String.class);
this.registerAlias("byte", Byte.class);
......
this.registerAlias("arraylist", ArrayList.class);
this.registerAlias("iterator", Iterator.class);
this.registerAlias("ResultSet", ResultSet.class);
}
//处理别名, 直接从保存有别名的hashMap中取出即可
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {
return null;
} else {
String key = string.toLowerCase(Locale.ENGLISH);
Class value;
if (this.TYPE_ALIASES.containsKey(key)) {
value = (Class)this.TYPE_ALIASES.get(key);
} else {
value = Resources.classForName(string);
}
return value;
}
} catch (ClassNotFoundException var4) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + var4, var4);
}
}
/**
* 配置文件中配置为package的时候, 会调用此方法,根据配置的报名去扫描javabean ,然后自动注册别名
* 默认会使用 Bean 的首字母小写的非限定类名来作为它的别名
* 也可在javabean 加上注解@Alias 来自定义别名, 例如: @Alias(user)
*/
public void registerAliases(String packageName) {
this.registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
resolverUtil.find(new IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
Iterator var5 = typeSet.iterator();
while(var5.hasNext()) {
Class<?> type = (Class)var5.next();
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
this.registerAlias(type);
}
}
}
public void registerAlias(Class<?> type) {
String alias = type.getSimpleName();
Alias aliasAnnotation = (Alias)type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
this.registerAlias(alias, type);
}
//这就是注册别名的本质方法, 其实就是向保存别名的hashMap新增值而已
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
} else {
String key = alias.toLowerCase(Locale.ENGLISH);
if (this.TYPE_ALIASES.containsKey(key) && this.TYPE_ALIASES.get(key) != null && !((Class)this.TYPE_ALIASES.get(key)).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + ((Class)this.TYPE_ALIASES.get(key)).getName() + "'.");
} else {
this.TYPE_ALIASES.put(key, value);
}
}
}
public void registerAlias(String alias, String value) {
try {
this.registerAlias(alias, Resources.classForName(value));
} catch (ClassNotFoundException var4) {
throw new TypeException("Error registering type alias " + alias + " for " + value + ". Cause: " + var4, var4);
}
}
public Map<String, Class<?>> getTypeAliases() {
return Collections.unmodifiableMap(this.TYPE_ALIASES);
}
从上面的源码可以看出,设置别名的原理就就是将其存入到一个map中,Mybatis默认给我们设置了不少别名