首先看settingsAsPropertiess(root.evalNode("settings"))这句代码,显而易见这句话获取了<configuration>下的<settings>节点。跟一下代码的实现:
private Properties settingsAsPropertiess(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
第5行将节点解析成键值对的形式(Properties是Hashtable的子类),看一下props的toString方法打印的内容:
{jdbcTypeForNull=OTHER, cacheEnabled=true, lazyLoadingEnabled=true, multipleResultSetsEnabled=true, autoMappingBehavior=PARTIAL, safeRowBoundsEnabled=false, mapUnderscoreToCamelCase=false, defaultStatementTimeout=25000, lazyLoadTriggerMethods=equals,clone,hashCode ,toString, logImpl=LOG4J, localCacheScope=SESSION, defaultExecutorType=SIMPLE, useGeneratedKeys=true, useColumnLabel=true}
可见settings里面的数据已经被解析成了Properties了。之后还有一步,<settings>标签下的每个<setting>中的name属性不是随便填写的,都是MyBatis支持的配置,因此需要对Properties里面的Key做一个校验,校验的代码就是第7行至第12行的代码,其中有一个name不是MyBatis支持的就会抛出异常,MyBatis初始化整体失败。
至于具体校验的是哪些Key,这就要跟一下第7行的代码了,首先是MetaClass.forClass(Configuration.class, localReflectorFactory),第二个实参是XMLConfigBuilder里面直接new出来的,它的实际类型为DefaultReflectorFactory,看一下forClass方法实现:
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
return new MetaClass(type, reflectorFactory);
}
看一下new MetaClass做了什么事:
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
显而易见,继续跟一下第3行的代码DefaultRelectorFactory的findForClass方法:
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
Reflector cached = reflectorMap.get(type);
if (cached == null) {
cached = new Reflector(type);
reflectorMap.put(type, cached);
}
return cached;
} else {
return new Reflector(type);
}
}
不管怎么样都会执行new Reflector(type)这一句代码,看一下此时做了什么事,注意传入的参数是Configuration的class对象:
public Reflector(Class<?> clazz) {
type = clazz;
addDefaultConstructor(clazz);
addGetMethods(clazz);
addSetMethods(clazz);
addFields(clazz);
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
这么多方法至于具体要看哪个,要注意的是之前XMLConfigBuilder里面对于Key的判断是"
!metaConfig.hasSetter(String.valueOf(key))
",代码的意思是判断的是否Key有set方法,那么显而易见这里要继续跟第5行的addSetMethods方法:
private void addSetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
String name = method.getName();
if (name.startsWith("set") && name.length() > 3) {
if (method.getParameterTypes().length == 1) {
name = PropertyNamer.methodToProperty(name);
addMethodConflict(conflictingSetters, name, method);
}
}
}
resolveSetterConflicts(conflictingSetters);
}
到这里应该很明显了,结论就是:<setting>的name属性对应的值,必须在Configuration类有相应的Setter,比如设置了一个属性useGenerateKeys方法,那么必须在Configuration类中有setUseGenerateKeys方法才行。
顺便说一下第13行有一个resolveSetterConflicts方法:
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
for (String propName : conflictingSetters.keySet()) {
List<Method> setters = conflictingSetters.get(propName);
Method firstMethod = setters.get(0);
if (setters.size() == 1) {
addSetMethod(propName, firstMethod);
} else {
Class<?> expectedType = getTypes.get(propName);
if (expectedType == null) {
throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
+ propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " +
"specification and can cause unpredicatble results.");
} else {
Iterator<Method> methods = setters.iterator();
Method setter = null;
while (methods.hasNext()) {
Method method = methods.next();
if (method.getParameterTypes().length == 1
&& expectedType.equals(method.getParameterTypes()[0])) {
setter = method;
break;
}
}
if (setter == null) {
throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
+ propName + " in class " + firstMethod.getDeclaringClass() + ". This breaks the JavaBeans " +
"specification and can cause unpredicatble results.");
}
addSetMethod(propName, setter);
}
}
}
}
Setter有可能在类中被重载导致有多个,此时取Setter中方法参数只有一个且参数类型与Getter一致的Setter。
接下来看addSetMethod(propName, firstMethod)方法:
private void addSetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
setMethods.put(name, new MethodInvoker(method));
Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
setTypes.put(name, typeToClass(paramTypes[0]));
}
}
将Setter中方法的名称存储到setMethods Map中。后面用这个Map进行判断是否配置正确!
接下来我们看一下如何设置settings作用.首先看loadCustomVfs(settings);这行代码!
private void loadCustomVfs(Properties props) throws ClassNotFoundException {
String value = props.getProperty("vfsImpl");
if (value != null) {
String[] clazzes = value.split(",");
for (String clazz : clazzes) {
if (!clazz.isEmpty()) {
@SuppressWarnings("unchecked")
Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz);
configuration.setVfsImpl(vfsImpl);
}
}
}
}
通过获取到设置的 vfsImpl,如果有则根据","切分成字符串。循环根据字符串实现类;我们来看一下setVfsImpl方法:
public void setVfsImpl(Class<? extends VFS> vfsImpl) {
if (vfsImpl != null) {
this.vfsImpl = vfsImpl;
VFS.addImplClass(this.vfsImpl);
}
}
VFS类用静态常量USER_IMPLEMENTATIONS添加了当前类。
这里涉及一个VFS类,没见怎么用过,这是一个服务器资源加载的类,默认提供了两种实现,【JBoss6VFS,DefaultVFS】并提供了用户扩展点, 我们这个配置就是扩展自定义的VFS的实现。
加载顺序: 自定义VFS实现 > 默认VFS实现 取第一个加载成功的。这里本人没用过,所以不加以详细说明,感兴趣的朋友可以去看一下。
接下来就是settingsElement(settings);这个我们后面讲解完typeAliases,plugins,objectFactory,objectWrapperFactory,reflectorFactory再说。
private void settingsElement(Properties props) throws Exception {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
@SuppressWarnings("unchecked")
Class<? extends Log> logImpl = (Class<? extends Log>)resolveClass(props.getProperty("logImpl"));
configuration.setLogImpl(logImpl);
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
看到这个方法的实现主要就是将之前解析出来的<settings>中的内容设置到Configuration中。大都有自己默认的配置,有些通过枚举进行转换,有些全局变量直接复制,这些作用后面再分析。