添加配置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标簽下的資訊:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPB1UNrRVT2Z0VhRHbHFmeWNjYvB3MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL3YjM5ATOzYTMzATOwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
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預設給我們設定了不少别名