需要注意的是,propertiesElement 方法是先解析 properties 节点的子节点内容,后再从文件系统或者网络读取属性配置,并将所有的属性及属性值都放入到 defaults 属性对象中。这就会存在同名属性覆盖的问题,也就是从文件系统,或者网络上读取到的属性及属性值会覆盖掉 properties 子节点中同名的属性和及值。比如上面配置中的jdbc.properties内容如下:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/myblog?... jdbc.username=root jdbc.password=1234与 properties 子节点内容合并后,结果如下:
如上,原jdbc.username值为coolblog,现在被覆盖为了root。同名属性覆盖的问题需要大家注意一下,其他的就没什么了,继续往下分析。
2.3 解析 settings 配置 2.3.1 settings 节点的解析过程settings 相关配置是 MyBatis 中非常重要的配置,这些配置用于调整 MyBatis 运行时的行为。settings 配置繁多,在对这些配置不熟悉的情况下,保持默认配置即可。关于 settings 相关配置,MyBatis 官网上进行了比较详细的描述,大家可以去了解一下。在本节中,暂时还用不到这些配置,所以即使不了解这些配置也没什么关系。下面先来看一个比较简单的配置,如下:
<settings> <setting value="true"/> <setting value="true"/> <setting value="PARTIAL"/> </settings>接下来,对照上面的配置,来分析源码。如下:
// -☆- XMLConfigBuilder private Properties settingsAsProperties(XNode context) { if (context == null) { return new Properties(); } // 获取 settings 子节点中的内容,getChildrenAsProperties 方法前面已分析过,这里不再赘述 Properties props = context.getChildrenAsProperties(); // 创建 Configuration 类的“元信息”对象 MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); for (Object key : props.keySet()) { // 检测 Configuration 中是否存在相关属性,不存在则抛出异常 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; }如上,settingsAsProperties 方法看起来并不复杂,不过这是一个假象。在上面的代码中出现了一个陌生的类MetaClass,这个类是用来做什么的呢?答案是用来解析目标类的一些元信息,比如类的成员变量,getter/setter 方法等。关于这个类的逻辑,待会我会详细解析。接下来,简单总结一下上面代码的逻辑。如下:
解析 settings 子节点的内容,并将解析结果转成 Properties 对象
为 Configuration 创建元信息对象
通过 MetaClass 检测 Configuration 中是否存在某个属性的 setter 方法,不存在则抛异常
若通过 MetaClass 的检测,则返回 Properties 对象,方法逻辑结束
下面,我们来重点关注一下第2步和第3步的流程。这两步流程对应的代码较为复杂,需要一点耐心阅读。好了,下面开始分析。
2.3.2 元信息对象创建过程元信息类MetaClass的构造方法为私有类型,所以不能直接创建,必须使用其提供的forClass方法进行创建。它的创建逻辑如下:
public class MetaClass { private final ReflectorFactory reflectorFactory; private final Reflector reflector; private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) { this.reflectorFactory = reflectorFactory; // 根据类型创建 Reflector this.reflector = reflectorFactory.findForClass(type); } public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) { // 调用构造方法 return new MetaClass(type, reflectorFactory); } // 省略其他方法 }上面的代码看起来很简单,不过这只是冰山一角。上面代码出现了两个新的类ReflectorFactory和Reflector,MetaClass 通过引入这些新类帮助它完成功能。下面我们看一下hasSetter方法的源码就知道是怎么回事了。
// -☆- MetaClass public boolean hasSetter(String name) { // 属性分词器,用于解析属性名 PropertyTokenizer prop = new PropertyTokenizer(name); // hasNext 返回 true,则表明 name 是一个复合属性,后面会进行分析 if (prop.hasNext()) { // 调用 reflector 的 hasSetter 方法 if (reflector.hasSetter(prop.getName())) { // 为属性创建创建 MetaClass MetaClass metaProp = metaClassForProperty(prop.getName()); // 再次调用 hasSetter return metaProp.hasSetter(prop.getChildren()); } else { return false; } } else { // 调用 reflector 的 hasSetter 方法 return reflector.hasSetter(prop.getName()); } }从上面的代码中,我们可以看出 MetaClass 中的 hasSetter 方法最终调用了 Reflector 的 hasSetter 方法。关于 Reflector 的 hasSetter 方法,这里先不分析,Reflector 这个类的逻辑较为复杂,本节会在随后进行详细说明。下面来简单介绍一下上面代码中出现的几个类:
ReflectorFactory -> 顾名思义,Reflector 的工厂类,兼有缓存 Reflector 对象的功能
Reflector -> 反射器,用于解析和存储目标类中的元信息
PropertyTokenizer -> 属性名分词器,用于处理较为复杂的属性名